diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..38838d563cc --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +tfb@techempower.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/frameworks/C++/libsniper/libs/core b/frameworks/C++/libsniper/libs/core new file mode 160000 index 00000000000..a792ecfebb0 --- /dev/null +++ b/frameworks/C++/libsniper/libs/core @@ -0,0 +1 @@ +Subproject commit a792ecfebb02f98bbdd8db232fba69f3f92907b3 diff --git a/frameworks/C++/userver/README.md b/frameworks/C++/userver/README.md index d10de17e403..8b1d83c4d2a 100755 --- a/frameworks/C++/userver/README.md +++ b/frameworks/C++/userver/README.md @@ -2,14 +2,11 @@ This is the [userver](https://github.com/userver-framework/userver) portion of a [benchmarking test suite](https://github.com/TechEmpower/FrameworkBenchmarks) comparing a variety of web development platforms. -This benchmarks comes in two configurations: **userver** and **userver-bare**, where both configurations use exactly the same handlers code, but **userver-bare** replaces default http implementation of **userver** with custom one. -You see, **userver** being feature-rich framework widely used in production comes with a lot of useful functionality built-in (metrics, dynamic configuring, logging/tracing, congestion control etc...) none of which is of any use in benchmarks; although most of that can be disabled via configs, some parts remain, and these parts aren't free. -The aim of **userver-bare** is to explore practical limits of lower-level **userver** functionality when performance is an absolute must, while still being idiomatic userver code. - ### Test Type Implementation Source Code * [Plaintext](userver_benchmark/controllers/plaintext/handler.cpp) * [Json](userver_benchmark/controllers/json/handler.cpp) +* [Fortunes](userver_benchmark/controllers/fortunes/handler.cpp) * [Single Database Query](userver_benchmark/controllers/single_query/handler.cpp) * [Multiple Database Queries](userver_benchmark/controllers/multiple_queries/handler.cpp) * [Database Updates](userver_benchmark/controllers/updates/handler.cpp) @@ -24,6 +21,10 @@ http://localhost:8080/plaintext http://localhost:8080/json +### Fortunes + +http://localhost:8080/fortunes + ### Single Database Query http://localhost:8080/db diff --git a/frameworks/C++/userver/benchmark_config.json b/frameworks/C++/userver/benchmark_config.json index aba5e7d5933..8a455b99a30 100755 --- a/frameworks/C++/userver/benchmark_config.json +++ b/frameworks/C++/userver/benchmark_config.json @@ -25,30 +25,6 @@ "display_name": "userver", "notes": "", "versus": "None" - }, - "bare": { - "json_url": "/json", - "plaintext_url": "/plaintext", - "db_url": "/db", - "query_url": "/queries?queries=", - "update_url": "/updates?queries=", - "cached_query_url": "/cached-queries?count=", - "fortune_url": "/fortunes", - "port": 8081, - "approach": "Realistic", - "classification": "Micro", - "database": "postgres", - "framework": "userver", - "language": "C++", - "flavor": "None", - "orm": "Micro", - "platform": "None", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "userver[bare]", - "notes": "", - "versus": "None" } } ] diff --git a/frameworks/C++/userver/config.toml b/frameworks/C++/userver/config.toml index 316860f74a1..424fd5d6457 100644 --- a/frameworks/C++/userver/config.toml +++ b/frameworks/C++/userver/config.toml @@ -18,21 +18,3 @@ orm = "Micro" platform = "None" webserver = "None" versus = "None" - -[bare] -urls.plaintext = "/plaintext" -urls.json = "/json" -urls.db = "/db" -urls.query = "/queries?queries=" -urls.update = "/updates?queries=" -urls.cached_query = "/cached-queries?count=" -urls.fortune = "/fortunes" -approach = "Realistic" -classification = "Micro" -database = "Postgres" -database_os = "Linux" -os = "Linux" -orm = "Micro" -platform = "None" -webserver = "None" -versus = "None" diff --git a/frameworks/C++/userver/userver-bare.dockerfile b/frameworks/C++/userver/userver-bare.dockerfile deleted file mode 100644 index a318a9601b7..00000000000 --- a/frameworks/C++/userver/userver-bare.dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM ghcr.io/userver-framework/ubuntu-22.04-userver-pg AS builder - -RUN apt update && \ - apt install -y lsb-release wget software-properties-common gnupg && \ - wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && ./llvm.sh 16 - -WORKDIR /src -RUN git clone https://github.com/userver-framework/userver.git && \ - cd userver && git checkout ec1a3b07793f8d4cd0968cd61d8e6079d667a1e7 - -COPY userver_benchmark/ ./ -RUN mkdir build && cd build && \ - cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \ - -DUSERVER_FEATURE_UTEST=0 \ - -DUSERVER_FEATURE_POSTGRESQL=1 \ - -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \ - -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native -flto=thin" -DCMAKE_C_FLAGS="-march=native -flto=thin" \ - -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 \ - -DUSERVER_LTO=0 .. && \ - make -j $(nproc) - -FROM builder AS runner -WORKDIR /app -COPY userver_configs/* ./ -COPY --from=builder /src/build/userver_techempower ./ - -EXPOSE 8081 -CMD ./userver_techempower -c ./static_config.yaml - diff --git a/frameworks/C++/userver/userver.dockerfile b/frameworks/C++/userver/userver.dockerfile index 9b45edd2418..e115816b324 100644 --- a/frameworks/C++/userver/userver.dockerfile +++ b/frameworks/C++/userver/userver.dockerfile @@ -6,7 +6,7 @@ RUN apt update && \ WORKDIR /src RUN git clone https://github.com/userver-framework/userver.git && \ - cd userver && git checkout ec1a3b07793f8d4cd0968cd61d8e6079d667a1e7 + cd userver && git checkout bdd5e1e03921ff378b062f86a189c3cfa3d66332 COPY userver_benchmark/ ./ RUN mkdir build && cd build && \ @@ -14,7 +14,7 @@ RUN mkdir build && cd build && \ -DUSERVER_FEATURE_UTEST=0 \ -DUSERVER_FEATURE_POSTGRESQL=1 \ -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \ - -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native -flto=thin" -DCMAKE_C_FLAGS="-march=native -flto=thin" \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" \ -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 \ -DUSERVER_LTO=0 .. && \ make -j $(nproc) diff --git a/frameworks/C++/userver/userver_benchmark/userver_techempower.cpp b/frameworks/C++/userver/userver_benchmark/userver_techempower.cpp index 6e994d3021c..af0a2b64842 100644 --- a/frameworks/C++/userver/userver_benchmark/userver_techempower.cpp +++ b/frameworks/C++/userver/userver_benchmark/userver_techempower.cpp @@ -46,20 +46,6 @@ class NoopTracingManager final userver::server::http::HttpResponse&) const final {} }; -class MinimalMiddlewarePipelineBuilder final - : public userver::server::middlewares::PipelineBuilder { - public: - static constexpr std::string_view kName{ - "minimal-middleware-pipeline-builder"}; - using userver::server::middlewares::PipelineBuilder::PipelineBuilder; - - private: - userver::server::middlewares::MiddlewaresList BuildPipeline( - userver::server::middlewares::MiddlewaresList) const override { - return {"userver-unknown-exceptions-handling-middleware"}; - } -}; - int Main(int argc, char* argv[]) { auto component_list = userver::components::MinimalServerComponentList() @@ -78,10 +64,9 @@ int Main(int argc, char* argv[]) { .Append() // cache component .Append() .Append() - // tracing and metrics tweaks + // tracing tweaks .Append() - .Append() - // bare + // bare (not used in the benchmark currently) .Append() .Append(); diff --git a/frameworks/C++/userver/userver_configs/static_config.yaml b/frameworks/C++/userver/userver_configs/static_config.yaml index 2bdcf1fbce5..ed793a694a7 100644 --- a/frameworks/C++/userver/userver_configs/static_config.yaml +++ b/frameworks/C++/userver/userver_configs/static_config.yaml @@ -1,8 +1,7 @@ # yaml components_manager: event_thread_pool: - threads: 9 - dedicated_timer_threads: 1 + threads: 8 coro_pool: initial_size: 10000 # Preallocate 10000 coroutines at startup. max_size: 300000 # Do not keep more than 300000 preallocated coroutines. @@ -12,7 +11,7 @@ components_manager: main-task-processor: # Make a task processor for CPU-bound couroutine tasks. thread_name: main-worker # OS will show the threads of this task processor with 'main-worker' prefix. - worker_threads: 46 + worker_threads: 48 guess-cpu-limit: true fs-task-processor: # Make a separate task processor for filesystem bound tasks. @@ -29,7 +28,6 @@ components_manager: handler-defaults: set_tracing_headers: false server-name: us - middleware-pipeline-builder: minimal-middleware-pipeline-builder simple-router: simple-server: port: 8081 @@ -63,7 +61,6 @@ components_manager: noop-tracing-manager: tracing-manager-locator: component-name: noop-tracing-manager - minimal-middleware-pipeline-builder: plaintext-handler: path: /plaintext diff --git a/frameworks/C/h2o/h2o.dockerfile b/frameworks/C/h2o/h2o.dockerfile index cfabc77bb3b..109c5e4375a 100644 --- a/frameworks/C/h2o/h2o.dockerfile +++ b/frameworks/C/h2o/h2o.dockerfile @@ -6,19 +6,28 @@ FROM "ubuntu:${UBUNTU_VERSION}" AS compile ARG DEBIAN_FRONTEND=noninteractive RUN apt-get -yqq update && \ + apt-get -yqq install \ + ca-certificates \ + curl \ + lsb-release && \ + install -dm755 /usr/share/postgresql-common/pgdg && \ + curl --fail -LSso /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" && \ + sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \ + https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > \ + /etc/apt/sources.list.d/pgdg.list' && \ + apt-get -yqq update && \ apt-get -yqq install \ autoconf \ bison \ cmake \ - curl \ flex \ g++ \ libbpfcc-dev \ libbrotli-dev \ libcap-dev \ - libicu-dev \ libnuma-dev \ - libreadline-dev \ + libpq-dev \ libssl-dev \ libtool \ libuv1-dev \ @@ -33,7 +42,7 @@ RUN apt-get -yqq update && \ ruby \ systemtap-sdt-dev -ARG H2O_VERSION=18b175f71ede08b50d3e5ae8303dacef3ea510fc +ARG H2O_VERSION=c54c63285b52421da2782f028022647fc2ea3dd1 WORKDIR /tmp/h2o-build RUN curl -LSs "https://github.com/h2o/h2o/archive/${H2O_VERSION}.tar.gz" | \ @@ -57,18 +66,6 @@ RUN curl -LSs "https://github.com/x86-64/mustache-c/archive/${MUSTACHE_C_REVISIO CFLAGS="-flto -march=native -mtune=native -O3" ./autogen.sh && \ make -j "$(nproc)" install -ARG POSTGRESQL_VERSION=a37bb7c13995b834095d9d064cad1023a6f99b10 - -WORKDIR /tmp/postgresql-build -RUN curl -LSs "https://github.com/postgres/postgres/archive/${POSTGRESQL_VERSION}.tar.gz" | \ - tar --strip-components=1 -xz && \ - CFLAGS="-flto -march=native -mtune=native -O3" ./configure \ - --includedir=/usr/local/include/postgresql \ - --prefix=/usr/local \ - --with-ssl=openssl && \ - make -j "$(nproc)" -C src/include install && \ - make -j "$(nproc)" -C src/interfaces/libpq install - ARG H2O_APP_PREFIX WORKDIR /tmp/build COPY CMakeLists.txt ../ @@ -85,15 +82,28 @@ RUN cmake \ FROM "ubuntu:${UBUNTU_VERSION}" +ARG POSTGRESQL_VERSION=17 + ARG DEBIAN_FRONTEND=noninteractive RUN apt-get -yqq update && \ + apt-get -yqq install \ + ca-certificates \ + curl \ + lsb-release && \ + install -dm755 /usr/share/postgresql-common/pgdg && \ + curl --fail -LSso /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" && \ + sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \ + https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > \ + /etc/apt/sources.list.d/pgdg.list' && \ + apt-get -yqq update && \ apt-get -yqq install \ libnuma1 \ - libyajl2 + libyajl2 \ + "postgresql-client-${POSTGRESQL_VERSION}" ARG H2O_APP_PREFIX COPY --from=compile "${H2O_APP_PREFIX}" "${H2O_APP_PREFIX}/" COPY --from=compile /usr/local/lib/libmustache_c.so "${H2O_APP_PREFIX}/lib/" -COPY --from=compile /usr/local/lib/libpq.so.5.17 "${H2O_APP_PREFIX}/lib/libpq.so.5" ENV LD_LIBRARY_PATH="${H2O_APP_PREFIX}/lib" EXPOSE 8080 ARG BENCHMARK_ENV diff --git a/frameworks/C/h2o/src/handlers/world.c b/frameworks/C/h2o/src/handlers/world.c index a87ee4c4282..84088a25d42 100644 --- a/frameworks/C/h2o/src/handlers/world.c +++ b/frameworks/C/h2o/src/handlers/world.c @@ -237,8 +237,10 @@ static int do_multiple_queries(bool do_update, bool use_cache, h2o_req_t *req) const size_t num_query = get_query_number(req); - // MAX_QUERIES is a relatively small number, so assume no overflow in the following - // arithmetic operations. + // MAX_QUERIES is a relatively small number, say less than or equal to UINT16_MAX, so assume no + // unsigned overflow in the following arithmetic operations. + static_assert(MAX_QUERIES <= UINT16_MAX, + "potential out-of-bounds memory accesses in the following code"); assert(num_query && num_query <= MAX_QUERIES); size_t base_size = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t); @@ -373,7 +375,7 @@ static void do_updates(multiple_query_ctx_t *query_ctx) for (size_t i = 0; i < query_ctx->num_result; i++) { query_ctx->res[i].id = htonl(query_ctx->res[i].id); query_ctx->res[i].random_number = - htonl(1 + get_random_number(MAX_ID, &query_ctx->ctx->random_seed)); + htonl(1 + get_random_number(MAX_ID, &query_ctx->ctx->random_seed)); paramFormats[2 * i] = 1; paramFormats[2 * i + 1] = 1; paramLengths[2 * i] = sizeof(query_ctx->res[i].id); diff --git a/frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile b/frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile deleted file mode 100644 index 2e7234ca98d..00000000000 --- a/frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build -RUN apt-get update -RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5 - -WORKDIR /app -COPY src . -RUN dotnet publish -c Release -o out /p:Driver=ado - -# Construct the actual image that will run -FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime - -WORKDIR /app -COPY --from=build /app/out ./ - -EXPOSE 8080 - -ENTRYPOINT ["./appMpower"] \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile b/frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile new file mode 100644 index 00000000000..a5bbf22ae6f --- /dev/null +++ b/frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile @@ -0,0 +1,51 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build +RUN apt-get update +RUN apt-get -yqq install clang zlib1g-dev +RUN apt-get update + +WORKDIR /app +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 + +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 update + +WORKDIR /odbc + +RUN curl -L -o mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.20/mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz +RUN tar -xvzf mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz +RUN cp mariadb-connector-odbc-3.1.20-debian-bookworm-amd64/lib/mariadb/libm* /usr/lib/ +RUN cp -r /odbc/mariadb-connector-odbc-3.1.20-debian-bookworm-amd64/lib/mariadb /usr/local/lib/mariadb +RUN rm mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz +#TODOLOCAL +#RUN curl -L -o mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.20/mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz +#RUN tar -xvzf mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz +#RUN cp mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64/lib/mariadb/libm* /usr/lib/ +#RUN cp -r /odbc/mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64/lib/mariadb /usr/local/lib/mariadb +#RUN rm mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz + +ENV PATH=/usr/local/unixODBC/bin:$PATH + +WORKDIR /etc/ +COPY odbcinst.ini . + +# Full PGO +ENV DOTNET_TieredPGO 1 +ENV DOTNET_TC_QuickJitForLoops 1 +ENV DOTNET_ReadyToRun 0 + +ENV ASPNETCORE_URLS http://+:8080 +WORKDIR /app +COPY --from=build /app/out ./ + +RUN cp /usr/lib/libm* /app + +EXPOSE 8080 + +ENTRYPOINT ["./appMpower"] \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile b/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile index c06865cc0b2..4080684bab6 100644 --- a/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile +++ b/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile @@ -4,7 +4,7 @@ RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5 WORKDIR /app COPY src . -RUN dotnet publish -c Release -o out /p:Driver=odbc +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 @@ -18,6 +18,12 @@ ENV PATH=/usr/local/unixODBC/bin:$PATH WORKDIR /etc/ COPY odbcinst.ini . +# Full PGO +ENV DOTNET_TieredPGO 1 +ENV DOTNET_TC_QuickJitForLoops 1 +ENV DOTNET_ReadyToRun 0 + +ENV ASPNETCORE_URLS http://+:8080 WORKDIR /app COPY --from=build /app/out ./ diff --git a/frameworks/CSharp/appmpower/appmpower.dockerfile b/frameworks/CSharp/appmpower/appmpower.dockerfile index 7f77873dfaf..3d521c490d8 100644 --- a/frameworks/CSharp/appmpower/appmpower.dockerfile +++ b/frameworks/CSharp/appmpower/appmpower.dockerfile @@ -4,11 +4,17 @@ RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5 WORKDIR /app COPY src . +#RUN dotnet publish appMpower/appMpower.csproj -c Release -o out 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 +# Full PGO +ENV DOTNET_TieredPGO 1 +ENV DOTNET_TC_QuickJitForLoops 1 +ENV DOTNET_ReadyToRun 0 +ENV ASPNETCORE_URLS http://+:8080 WORKDIR /app COPY --from=build /app/out ./ diff --git a/frameworks/CSharp/appmpower/benchmark_config.json b/frameworks/CSharp/appmpower/benchmark_config.json index 6c22c0c65af..46dc7e7c00f 100644 --- a/frameworks/CSharp/appmpower/benchmark_config.json +++ b/frameworks/CSharp/appmpower/benchmark_config.json @@ -39,11 +39,11 @@ "webserver": "Kestrel", "os": "Linux", "database_os": "Linux", - "display_name": "appMpower [aot-no-reflection,odbc]", + "display_name": "appMpower [aot-no-reflection,pg,odbc]", "notes": "", "versus": "aspnetcore-minimal" }, - "ado-pg": { + "odbc-my": { "db_url": "/db", "query_url": "/queries?c=", "update_url": "/updates?c=", @@ -52,7 +52,7 @@ "port": 8080, "approach": "Realistic", "classification": "Platform", - "database": "Postgres", + "database": "MySQL", "framework": "appmpower", "language": "C#", "orm": "Raw", @@ -61,7 +61,7 @@ "webserver": "Kestrel", "os": "Linux", "database_os": "Linux", - "display_name": "appMpower [aot-no-reflection,ado]", + "display_name": "appMpower [aot-no-reflection,my,odbc]", "notes": "", "versus": "aspnetcore-minimal" } diff --git a/frameworks/CSharp/appmpower/config.toml b/frameworks/CSharp/appmpower/config.toml index d500b2df989..a3747682191 100644 --- a/frameworks/CSharp/appmpower/config.toml +++ b/frameworks/CSharp/appmpower/config.toml @@ -30,7 +30,7 @@ platform = ".NET" webserver = "Kestrel" versus = "aspnetcore-minimal" -[ado-pg] +[odbc-my] urls.db = "/db" urls.query = "/queries?c=" urls.update = "/updates?c=" @@ -38,7 +38,7 @@ urls.fortune = "/fortunes" urls.cached_query = "/cached-worlds?c=" approach = "Realistic" classification = "Micro" -database = "Postgres" +database = "MySQL" database_os = "Linux" os = "Linux" orm = "Raw" diff --git a/frameworks/CSharp/appmpower/odbcinst.ini b/frameworks/CSharp/appmpower/odbcinst.ini index c6260e38b93..544ba2067c1 100644 --- a/frameworks/CSharp/appmpower/odbcinst.ini +++ b/frameworks/CSharp/appmpower/odbcinst.ini @@ -5,6 +5,7 @@ Pooling=0 [ODBC Drivers] PostgreSQL = Installed +MariaDB = Installed ; ; odbcinst.ini @@ -15,7 +16,16 @@ Description=ODBC for PostgreSQL ; in version 08.x. Note that the library can also be installed under an other ; path than /usr/local/lib/ following your installation. ; This is the standard location used by apt-get install -y unixodbc +;ON SERVER Driver = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so +;TODOLOCAL: ON MAC +;Driver =/usr/lib/aarch64-linux-gnu/odbc/psqlodbcw.so + ;Driver =/usr/local/pgsqlodbc/lib/psqlodbcw.so Threading = 0 CPTimeout = 0 + +[MariaDB] +Description=MariaDB ODBC for MySQL +Driver = /usr/lib/libmaodbc.so +Threading = 0 \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs b/frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs deleted file mode 100644 index f7c78b6dd65..00000000000 --- a/frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json; - -namespace appMpower -{ - public class CachedWorldSerializer : Kestrel.IJsonSerializer - { - public void Serialize(Utf8JsonWriter utf8JsonWriter, CachedWorld world) - { - utf8JsonWriter.WriteStartObject(); - utf8JsonWriter.WriteNumber("id", world.Id); - utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber); - utf8JsonWriter.WriteEndObject(); - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Data/DbConnection.cs b/frameworks/CSharp/appmpower/src/Data/DbConnection.cs deleted file mode 100644 index 916e251a9c8..00000000000 --- a/frameworks/CSharp/appmpower/src/Data/DbConnection.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System.Collections.Concurrent; -using System.Data; -using System.Threading.Tasks; - -namespace appMpower.Data -{ - public class DbConnection : IDbConnection - { - private string _connectionString; - internal InternalConnection _internalConnection; - - public DbConnection() - { - _connectionString = DbProviderFactory.ConnectionString; - } - - public DbConnection(string connectionString) - { - _connectionString = connectionString; - } - - internal ConcurrentDictionary DbCommands - { - get - { - return _internalConnection.DbCommands; - } - set - { - _internalConnection.DbCommands = value; - } - } - - public short Number - { - get - { - return _internalConnection.Number; - } - set - { - _internalConnection.Number = value; - } - } - - public IDbConnection Connection - { - get - { - return _internalConnection.DbConnection; - } - set - { - _internalConnection.DbConnection = value; - } - } - - public string ConnectionString - { - get - { - return _internalConnection.DbConnection.ConnectionString; - } - set - { - _internalConnection.DbConnection.ConnectionString = value; - } - } - - public int ConnectionTimeout - { - get - { - return _internalConnection.DbConnection.ConnectionTimeout; - } - } - - public string Database - { - get - { - return _internalConnection.DbConnection.Database; - } - } - - public ConnectionState State - { - get - { - if (_internalConnection is null) return ConnectionState.Closed; - return _internalConnection.DbConnection.State; - } - } - - public IDbTransaction BeginTransaction() - { - return _internalConnection.DbConnection.BeginTransaction(); - } - - public IDbTransaction BeginTransaction(IsolationLevel il) - { - return _internalConnection.DbConnection.BeginTransaction(il); - } - - public void ChangeDatabase(string databaseName) - { - _internalConnection.DbConnection.ChangeDatabase(databaseName); - } - - public void Close() - { - _internalConnection.DbConnection.Close(); - } - - public async Task CloseAsync() - { - await (_internalConnection.DbConnection as System.Data.Common.DbConnection).CloseAsync(); - } - - public IDbCommand CreateCommand() - { - return _internalConnection.DbConnection.CreateCommand(); - } - - public void Open() - { - if (_internalConnection.DbConnection.State == ConnectionState.Closed) - { - _internalConnection.DbConnection.Open(); - } - } - - public void Dispose() - { -#if ADO - _internalConnection.DbConnection.Dispose(); - _internalConnection.Dispose(); -#else - DbConnections.Release(_internalConnection); -#endif - } - - public async Task OpenAsync() - { -#if ADO && POSTGRESQL - _internalConnection = new(); - _internalConnection.DbConnection = new Npgsql.NpgsqlConnection(_connectionString); -#else - if (_internalConnection is null) - { - _internalConnection = await DbConnections.GetConnection(_connectionString); - } -#endif - - if (_internalConnection.DbConnection.State == ConnectionState.Closed) - { - await (_internalConnection.DbConnection as System.Data.Common.DbConnection).OpenAsync(); - } - } - - internal DbCommand GetCommand(string commandText, CommandType commandType, DbCommand dbCommand) - { -#if ADO - dbCommand.Command = _internalConnection.DbConnection.CreateCommand(); - dbCommand.Command.CommandText = commandText; - dbCommand.Command.CommandType = commandType; - dbCommand.DbConnection = this; -#else - DbCommand internalCommand; - - if (_internalConnection.DbCommands.TryRemove(commandText, out internalCommand)) - { - dbCommand.Command = internalCommand.Command; - dbCommand.DbConnection = internalCommand.DbConnection; - } - else - { - dbCommand.Command = _internalConnection.DbConnection.CreateCommand(); - dbCommand.Command.CommandText = commandText; - dbCommand.Command.CommandType = commandType; - dbCommand.DbConnection = this; - - //For non odbc drivers like Npgsql which do not support Prepare - dbCommand.Command.Prepare(); - - //Console.WriteLine("prepare pool connection: " + this._internalConnection.Number + " for command " + _internalConnection.DbCommands.Count); - } -#endif - - return dbCommand; - } - - public void ReleaseCommand(DbCommand dbCommand) - { -#if !ADO - _internalConnection.DbCommands.TryAdd(dbCommand.CommandText, dbCommand); -#endif - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Data/DbConnections.cs b/frameworks/CSharp/appmpower/src/Data/DbConnections.cs deleted file mode 100644 index 0b513a3d47f..00000000000 --- a/frameworks/CSharp/appmpower/src/Data/DbConnections.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading.Tasks; - -namespace appMpower.Data -{ - public static class DbConnections - { - private static bool _connectionsCreated = false; - private static short _createdConnections = 0; - private static short _maxConnections = 500; - - private static ConcurrentStack _stack = new(); - private static ConcurrentQueue> _waitingQueue = new(); - - public static async Task GetConnection(string connectionString) - { - InternalConnection internalConnection = null; - - if (_connectionsCreated) - { - if (!_stack.TryPop(out internalConnection)) - { - internalConnection = await GetDbConnectionAsync(); - } - - return internalConnection; - } - else - { - internalConnection = new InternalConnection(); - internalConnection.DbConnection = new System.Data.Odbc.OdbcConnection(connectionString); - - _createdConnections++; - - if (_createdConnections == _maxConnections) _connectionsCreated = true; - - internalConnection.Number = _createdConnections; - internalConnection.DbCommands = new ConcurrentDictionary(); - //Console.WriteLine("opened connection number: " + dbConnection.Number); - - return internalConnection; - } - } - - public static Task GetDbConnectionAsync() - { - var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - _waitingQueue.Enqueue(taskCompletionSource); - return taskCompletionSource.Task; - } - - public static void Release(InternalConnection internalConnection) - { - TaskCompletionSource taskCompletionSource; - - if (_waitingQueue.TryDequeue(out taskCompletionSource)) - { - taskCompletionSource.SetResult(internalConnection); - } - else - { - _stack.Push(internalConnection); - } - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Data/DbProviderFactory.cs b/frameworks/CSharp/appmpower/src/Data/DbProviderFactory.cs deleted file mode 100644 index 98a4aa67cb6..00000000000 --- a/frameworks/CSharp/appmpower/src/Data/DbProviderFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Data; - -namespace appMpower.Data -{ - public static class DbProviderFactory - { -#if MYSQL - public const string ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1"; -#elif ADO - public const string ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=0;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; - //public const string ConnectionString = "Server=localhost;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"; -#else - public const string ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false"; - //public const string ConnectionString = "Driver={PostgreSQL};Server=localhost;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false"; -#endif - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Data/InternalConnection.cs b/frameworks/CSharp/appmpower/src/Data/InternalConnection.cs deleted file mode 100644 index 25cecfeb4f8..00000000000 --- a/frameworks/CSharp/appmpower/src/Data/InternalConnection.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Concurrent; -using System.Data; - -namespace appMpower.Data -{ - public class InternalConnection : System.IDisposable - { - public short Number { get; set; } - public IDbConnection DbConnection { get; set; } - public ConcurrentDictionary DbCommands { get; set; } - - public void Dispose() - { - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/HttpApplication.cs b/frameworks/CSharp/appmpower/src/HttpApplication.cs deleted file mode 100644 index 1eac250ede5..00000000000 --- a/frameworks/CSharp/appmpower/src/HttpApplication.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting.Server; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using appMpower.Kestrel; - -namespace appMpower -{ - public class HttpApplication : IHttpApplication - { - public static readonly byte[] _plainText = Encoding.UTF8.GetBytes("Hello, World!"); - private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer(); - private readonly static WorldSerializer _worldSerializer = new WorldSerializer(); - private readonly static CachedWorldSerializer _cachedWorldSerializer = new CachedWorldSerializer(); - - public IFeatureCollection CreateContext(IFeatureCollection featureCollection) - { - return featureCollection; - } - - public async Task ProcessRequestAsync(IFeatureCollection featureCollection) - { - var request = featureCollection as IHttpRequestFeature; - var httpResponse = featureCollection as IHttpResponseFeature; - var httpResponseBody = featureCollection as IHttpResponseBodyFeature; - - PathString pathString = request.Path; - - if (pathString.HasValue) - { - int pathStringLength = pathString.Value.Length; - string pathStringStart = pathString.Value.Substring(1, 1); - - if (pathStringLength == 10 && pathStringStart == "p") - { - //await PlainText.RenderAsync(httpResponse.Headers, httpResponseBody.Writer, _plainText); - PlainText.Render(httpResponse.Headers, httpResponseBody, _plainText); - return; - } - else if (pathStringLength == 5 && pathStringStart == "j") - { - Json.RenderOne(httpResponse.Headers, httpResponseBody.Writer, new JsonMessage { message = "Hello, World!" }, _jsonMessageSerializer); - return; - } - else if (pathStringLength == 3 && pathStringStart == "d") - { - Json.RenderOne(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadSingleQueryRow(), _worldSerializer); - return; - } - else if (pathStringLength == 8 && pathStringStart == "q") - { - int count = 1; - - if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1) - { - count = 1; - } - else if (count > 500) - { - count = 500; - } - -#if ADO - Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleQueriesRows(count), _worldSerializer); -#else - Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.ReadMultipleRows(count), _worldSerializer); -#endif - - return; - } - else if (pathStringLength == 9 && pathStringStart == "f") - { - await FortunesView.Render(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadFortunesRows()); - return; - } - else if (pathStringLength == 8 && pathStringStart == "u") - { - int count = 1; - - if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1) - { - count = 1; - } - else if (count > 500) - { - count = 500; - } - - Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleUpdatesRows(count), _worldSerializer); - return; - } - else if (pathStringLength == 14 && pathStringStart == "c") - { - int count = 1; - - if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1) - { - count = 1; - } - else if (count > 500) - { - count = 500; - } - - Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadCachedQueries(count), _cachedWorldSerializer); - return; - } - } - } - - public void DisposeContext(IFeatureCollection featureCollection, Exception exception) - { - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/JsonMessage.cs b/frameworks/CSharp/appmpower/src/JsonMessage.cs deleted file mode 100644 index 24b78265baa..00000000000 --- a/frameworks/CSharp/appmpower/src/JsonMessage.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace appMpower -{ - public struct JsonMessage - { - public string message { get; set; } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Kestrel/Json.cs b/frameworks/CSharp/appmpower/src/Kestrel/Json.cs deleted file mode 100644 index c81028e2a9b..00000000000 --- a/frameworks/CSharp/appmpower/src/Kestrel/Json.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO.Pipelines; -using System.Text.Json; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; - -namespace appMpower.Kestrel -{ - public static class Json - { - private readonly static KeyValuePair _headerServer = - new KeyValuePair("Server", "k"); - private readonly static KeyValuePair _headerContentType = - new KeyValuePair("Content-Type", "application/json"); - - [ThreadStatic] - private static Utf8JsonWriter _utf8JsonWriter; - - public static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions - { - SkipValidation = true - }; - - public static void RenderOne(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T t, IJsonSerializer jsonSerializer) - { - headerDictionary.Add(_headerServer); - headerDictionary.Add(_headerContentType); - - Utf8JsonWriter utf8JsonWriter = _utf8JsonWriter ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true }); - utf8JsonWriter.Reset(pipeWriter); - - jsonSerializer.Serialize(utf8JsonWriter, t); - utf8JsonWriter.Flush(); - headerDictionary.Add(new KeyValuePair("Content-Length", utf8JsonWriter.BytesCommitted.ToString())); - } - - public static void RenderMany(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T[] tArray, IJsonSerializer jsonSerializer) - { - headerDictionary.Add(_headerServer); - headerDictionary.Add(_headerContentType); - - Utf8JsonWriter utf8JsonWriter = _utf8JsonWriter ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true }); - utf8JsonWriter.Reset(pipeWriter); - - utf8JsonWriter.WriteStartArray(); - - foreach (var t in tArray) - { - jsonSerializer.Serialize(utf8JsonWriter, t); - } - - utf8JsonWriter.WriteEndArray(); - utf8JsonWriter.Flush(); - headerDictionary.Add(new KeyValuePair("Content-Length", utf8JsonWriter.BytesCommitted.ToString())); - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs b/frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs deleted file mode 100644 index e7ead33aa2e..00000000000 --- a/frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.IO.Pipelines; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; -using Microsoft.AspNetCore.Http.Features; - -namespace appMpower.Kestrel -{ - public static class PlainText - { - private readonly static KeyValuePair _headerServer = - new KeyValuePair("Server", new StringValues("k")); - private readonly static KeyValuePair _headerContentType = - new KeyValuePair("Content-Type", new StringValues("text/plain")); - - public static async Task RenderAsync(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, ReadOnlyMemory utf8String) - { - headerDictionary.Add(_headerServer); - headerDictionary.Add(_headerContentType); - headerDictionary.Add(new KeyValuePair("Content-Length", utf8String.Length.ToString())); - - await pipeWriter.WriteAsync(utf8String); - pipeWriter.Complete(); - } - - public static void Render(IHeaderDictionary headerDictionary, IHttpResponseBodyFeature httpResponseBodyFeature, byte[] utf8String) - { - headerDictionary.Add(_headerServer); - headerDictionary.Add(_headerContentType); - int length = utf8String.Length; - headerDictionary.Add(new KeyValuePair("Content-Length", length.ToString())); - - httpResponseBodyFeature.Stream.Write(utf8String, 0, length); - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Kestrel/ServiceProvider.cs b/frameworks/CSharp/appmpower/src/Kestrel/ServiceProvider.cs deleted file mode 100644 index 513174e68b9..00000000000 --- a/frameworks/CSharp/appmpower/src/Kestrel/ServiceProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; - -namespace appMpower.Kestrel -{ - public class ServiceProvider : ISupportRequiredService, IServiceProvider - { - public object GetRequiredService(Type serviceType) - { - return GetService(serviceType); - } - - public object GetService(Type serviceType) - { - if (serviceType == typeof(ILoggerFactory)) - { - return NullLoggerFactory.Instance; - } - - return null; - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs b/frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs deleted file mode 100644 index 55ca29c4984..00000000000 --- a/frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs +++ /dev/null @@ -1,257 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Primitives; -using Microsoft.Extensions.Caching.Memory; - -namespace appMpower.Memory -{ - internal sealed partial class CacheEntry : ICacheEntry - { - private static readonly Action ExpirationCallback = ExpirationTokensExpired; - - private readonly MemoryCache _cache; - - private CacheEntryTokens _tokens; // might be null if user is not using the tokens or callbacks - private TimeSpan? _absoluteExpirationRelativeToNow; - private TimeSpan? _slidingExpiration; - private long? _size; - private CacheEntry _previous; // this field is not null only before the entry is added to the cache and tracking is enabled - private object _value; - private CacheEntryState _state; - - internal CacheEntry(object key, MemoryCache memoryCache) - { - Key = key ?? throw new ArgumentNullException(nameof(key)); - _cache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache)); - _previous = memoryCache.TrackLinkedCacheEntries ? CacheEntryHelper.EnterScope(this) : null; - _state = new CacheEntryState(CacheItemPriority.Normal); - } - - /// - /// Gets or sets an absolute expiration date for the cache entry. - /// - public DateTimeOffset? AbsoluteExpiration { get; set; } - - /// - /// Gets or sets an absolute expiration time, relative to now. - /// - public TimeSpan? AbsoluteExpirationRelativeToNow - { - get => _absoluteExpirationRelativeToNow; - set - { - // this method does not set AbsoluteExpiration as it would require calling Clock.UtcNow twice: - // once here and once in MemoryCache.SetEntry - - if (value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException( - nameof(AbsoluteExpirationRelativeToNow), - value, - "The relative expiration value must be positive."); - } - - _absoluteExpirationRelativeToNow = value; - } - } - - /// - /// Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. - /// This will not extend the entry lifetime beyond the absolute expiration (if set). - /// - public TimeSpan? SlidingExpiration - { - get => _slidingExpiration; - set - { - if (value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException( - nameof(SlidingExpiration), - value, - "The sliding expiration value must be positive."); - } - - _slidingExpiration = value; - } - } - - /// - /// Gets the instances which cause the cache entry to expire. - /// - public IList ExpirationTokens => GetOrCreateTokens().ExpirationTokens; - - /// - /// Gets or sets the callbacks will be fired after the cache entry is evicted from the cache. - /// - public IList PostEvictionCallbacks => GetOrCreateTokens().PostEvictionCallbacks; - - /// - /// Gets or sets the priority for keeping the cache entry in the cache during a - /// memory pressure triggered cleanup. The default is . - /// - public CacheItemPriority Priority { get => _state.Priority; set => _state.Priority = value; } - - /// - /// Gets or sets the size of the cache entry value. - /// - public long? Size - { - get => _size; - set - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative."); - } - - _size = value; - } - } - - public object Key { get; private set; } - - public object Value - { - get => _value; - set - { - _value = value; - _state.IsValueSet = true; - } - } - - internal DateTimeOffset LastAccessed { get; set; } - - internal EvictionReason EvictionReason { get => _state.EvictionReason; private set => _state.EvictionReason = value; } - - public void Dispose() - { - if (!_state.IsDisposed) - { - _state.IsDisposed = true; - - if (_cache.TrackLinkedCacheEntries) - { - CacheEntryHelper.ExitScope(this, _previous); - } - - // Don't commit or propagate options if the CacheEntry Value was never set. - // We assume an exception occurred causing the caller to not set the Value successfully, - // so don't use this entry. - if (_state.IsValueSet) - { - _cache.SetEntry(this); - - if (_previous != null && CanPropagateOptions()) - { - PropagateOptions(_previous); - } - } - - _previous = null; // we don't want to root unnecessary objects - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling - internal bool CheckExpired(in DateTimeOffset now) - => _state.IsExpired - || CheckForExpiredTime(now) - || (_tokens != null && _tokens.CheckForExpiredTokens(this)); - - internal void SetExpired(EvictionReason reason) - { - if (EvictionReason == EvictionReason.None) - { - EvictionReason = reason; - } - _state.IsExpired = true; - _tokens?.DetachTokens(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling - private bool CheckForExpiredTime(in DateTimeOffset now) - { - if (!AbsoluteExpiration.HasValue && !_slidingExpiration.HasValue) - { - return false; - } - - return FullCheck(now); - - bool FullCheck(in DateTimeOffset offset) - { - if (AbsoluteExpiration.HasValue && AbsoluteExpiration.Value <= offset) - { - SetExpired(EvictionReason.Expired); - return true; - } - - if (_slidingExpiration.HasValue - && (offset - LastAccessed) >= _slidingExpiration) - { - SetExpired(EvictionReason.Expired); - return true; - } - - return false; - } - } - - internal void AttachTokens() => _tokens?.AttachTokens(this); - - private static void ExpirationTokensExpired(object obj) - { - // start a new thread to avoid issues with callbacks called from RegisterChangeCallback - Task.Factory.StartNew(state => - { - var entry = (CacheEntry)state; - entry.SetExpired(EvictionReason.TokenExpired); - entry._cache.EntryExpired(entry); - }, obj, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); - } - - internal void InvokeEvictionCallbacks() => _tokens?.InvokeEvictionCallbacks(this); - - // this simple check very often allows us to avoid expensive call to PropagateOptions(CacheEntryHelper.Current) - [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling - internal bool CanPropagateOptions() => (_tokens != null && _tokens.CanPropagateTokens()) || AbsoluteExpiration.HasValue; - - internal void PropagateOptions(CacheEntry parent) - { - if (parent == null) - { - return; - } - - // Copy expiration tokens and AbsoluteExpiration to the cache entries hierarchy. - // We do this regardless of it gets cached because the tokens are associated with the value we'll return. - _tokens?.PropagateTokens(parent); - - if (AbsoluteExpiration.HasValue) - { - if (!parent.AbsoluteExpiration.HasValue || AbsoluteExpiration < parent.AbsoluteExpiration) - { - parent.AbsoluteExpiration = AbsoluteExpiration; - } - } - } - - private CacheEntryTokens GetOrCreateTokens() - { - if (_tokens != null) - { - return _tokens; - } - - CacheEntryTokens result = new CacheEntryTokens(); - return Interlocked.CompareExchange(ref _tokens, result, null) ?? result; - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs b/frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs deleted file mode 100644 index 71d07fe24c5..00000000000 --- a/frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Threading; - -namespace appMpower.Memory -{ - internal static class CacheEntryHelper - { - private static readonly AsyncLocal _current = new AsyncLocal(); - - internal static CacheEntry Current - { - get => _current.Value; - private set => _current.Value = value; - } - - internal static CacheEntry EnterScope(CacheEntry current) - { - CacheEntry previous = Current; - Current = current; - return previous; - } - - internal static void ExitScope(CacheEntry current, CacheEntry previous) - { - Debug.Assert(Current == current, "Entries disposed in invalid order"); - Current = previous; - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs b/frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs deleted file mode 100644 index 15d59ead9c1..00000000000 --- a/frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Extensions.Caching.Memory; - -namespace appMpower.Memory -{ - internal sealed partial class CacheEntry - { - // this type exists just to reduce CacheEntry size by replacing many enum & boolean fields with one of a size of Int32 - private struct CacheEntryState - { - private byte _flags; - private byte _evictionReason; - private byte _priority; - - internal CacheEntryState(CacheItemPriority priority) : this() => _priority = (byte)priority; - - internal bool IsDisposed - { - get => ((Flags)_flags & Flags.IsDisposed) != 0; - set => SetFlag(Flags.IsDisposed, value); - } - - internal bool IsExpired - { - get => ((Flags)_flags & Flags.IsExpired) != 0; - set => SetFlag(Flags.IsExpired, value); - } - - internal bool IsValueSet - { - get => ((Flags)_flags & Flags.IsValueSet) != 0; - set => SetFlag(Flags.IsValueSet, value); - } - - internal EvictionReason EvictionReason - { - get => (EvictionReason)_evictionReason; - set => _evictionReason = (byte)value; - } - - internal CacheItemPriority Priority - { - get => (CacheItemPriority)_priority; - set => _priority = (byte)value; - } - - private void SetFlag(Flags option, bool value) => _flags = (byte)(value ? (_flags | (byte)option) : (_flags & ~(byte)option)); - - [Flags] - private enum Flags : byte - { - Default = 0, - IsValueSet = 1 << 0, - IsExpired = 1 << 1, - IsDisposed = 1 << 2, - } - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs b/frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs deleted file mode 100644 index 247630639e7..00000000000 --- a/frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Primitives; -using Microsoft.Extensions.Caching.Memory; - -namespace appMpower.Memory -{ - internal sealed partial class CacheEntry - { - // this type exists just to reduce average CacheEntry size - // which typically is not using expiration tokens or callbacks - private sealed class CacheEntryTokens - { - private List _expirationTokens; - private List _expirationTokenRegistrations; - private List _postEvictionCallbacks; // this is not really related to tokens, but was moved here to shrink typical CacheEntry size - - internal List ExpirationTokens => _expirationTokens ??= new List(); - internal List PostEvictionCallbacks => _postEvictionCallbacks ??= new List(); - - internal void AttachTokens(CacheEntry cacheEntry) - { - if (_expirationTokens != null) - { - lock (this) - { - for (int i = 0; i < _expirationTokens.Count; i++) - { - IChangeToken expirationToken = _expirationTokens[i]; - if (expirationToken.ActiveChangeCallbacks) - { - _expirationTokenRegistrations ??= new List(1); - IDisposable registration = expirationToken.RegisterChangeCallback(ExpirationCallback, cacheEntry); - _expirationTokenRegistrations.Add(registration); - } - } - } - } - } - - internal bool CheckForExpiredTokens(CacheEntry cacheEntry) - { - if (_expirationTokens != null) - { - for (int i = 0; i < _expirationTokens.Count; i++) - { - IChangeToken expiredToken = _expirationTokens[i]; - if (expiredToken.HasChanged) - { - cacheEntry.SetExpired(EvictionReason.TokenExpired); - return true; - } - } - } - return false; - } - - internal bool CanPropagateTokens() => _expirationTokens != null; - - internal void PropagateTokens(CacheEntry parentEntry) - { - if (_expirationTokens != null) - { - lock (this) - { - lock (parentEntry.GetOrCreateTokens()) - { - foreach (IChangeToken expirationToken in _expirationTokens) - { - parentEntry.AddExpirationToken(expirationToken); - } - } - } - } - } - - internal void DetachTokens() - { - // _expirationTokenRegistrations is not checked for null, because AttachTokens might initialize it under lock - // instead we are checking for _expirationTokens, because if they are not null, then _expirationTokenRegistrations might also be not null - if (_expirationTokens != null) - { - lock (this) - { - List registrations = _expirationTokenRegistrations; - if (registrations != null) - { - _expirationTokenRegistrations = null; - for (int i = 0; i < registrations.Count; i++) - { - IDisposable registration = registrations[i]; - registration.Dispose(); - } - } - } - } - } - - internal void InvokeEvictionCallbacks(CacheEntry cacheEntry) - { - if (_postEvictionCallbacks != null) - { - Task.Factory.StartNew(state => InvokeCallbacks((CacheEntry)state), cacheEntry, - CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); - } - } - - private static void InvokeCallbacks(CacheEntry entry) - { - List callbackRegistrations = Interlocked.Exchange(ref entry._tokens._postEvictionCallbacks, null); - - if (callbackRegistrations == null) - { - return; - } - - for (int i = 0; i < callbackRegistrations.Count; i++) - { - PostEvictionCallbackRegistration registration = callbackRegistrations[i]; - - try - { - registration.EvictionCallback?.Invoke(entry.Key, entry.Value, entry.EvictionReason, registration.State); - } - catch (Exception e) - { - // This will be invoked on a background thread, don't let it throw. - entry._cache._logger.LogError(e, "EvictionCallback invoked failed"); - } - } - } - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs b/frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs deleted file mode 100644 index df4f7063cf0..00000000000 --- a/frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs +++ /dev/null @@ -1,520 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Internal; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Caching.Memory; - -namespace appMpower.Memory -{ - /// - /// An implementation of using a dictionary to - /// store its entries. - /// - public class MemoryCache : IMemoryCache - { - internal readonly ILogger _logger; - - private readonly MemoryCacheOptions _options; - private readonly ConcurrentDictionary _entries; - - private long _cacheSize; - private bool _disposed; - private DateTimeOffset _lastExpirationScan; - - /// - /// Creates a new instance. - /// - /// The options of the cache. - public MemoryCache(IOptions optionsAccessor) - : this(optionsAccessor, NullLoggerFactory.Instance) { } - - /// - /// Creates a new instance. - /// - /// The options of the cache. - /// The factory used to create loggers. - public MemoryCache(IOptions optionsAccessor, ILoggerFactory loggerFactory) - { - if (optionsAccessor == null) - { - throw new ArgumentNullException(nameof(optionsAccessor)); - } - - if (loggerFactory == null) - { - throw new ArgumentNullException(nameof(loggerFactory)); - } - - _options = optionsAccessor.Value; - //_logger = loggerFactory.CreateLogger(); - _logger = loggerFactory.CreateLogger("MemoryCache"); - - _entries = new ConcurrentDictionary(); - - if (_options.Clock == null) - { - _options.Clock = new SystemClock(); - } - - _lastExpirationScan = _options.Clock.UtcNow; - TrackLinkedCacheEntries = _options.TrackLinkedCacheEntries; // we store the setting now so it's consistent for entire MemoryCache lifetime - } - - /// - /// Cleans up the background collection events. - /// - ~MemoryCache() => Dispose(false); - - /// - /// Gets the count of the current entries for diagnostic purposes. - /// - public int Count => _entries.Count; - - // internal for testing - internal long Size { get => Interlocked.Read(ref _cacheSize); } - - internal bool TrackLinkedCacheEntries { get; } - - private ICollection> EntriesCollection => _entries; - - /// - public ICacheEntry CreateEntry(object key) - { - CheckDisposed(); - ValidateCacheKey(key); - - return new CacheEntry(key, this); - } - - internal void SetEntry(CacheEntry entry) - { - if (_disposed) - { - // No-op instead of throwing since this is called during CacheEntry.Dispose - return; - } - - if (_options.SizeLimit.HasValue && !entry.Size.HasValue) - { - //throw new InvalidOperationException(SR.Format(SR.CacheEntryHasEmptySize, nameof(entry.Size), nameof(_options.SizeLimit))); - throw new InvalidOperationException(); - } - - DateTimeOffset utcNow = _options.Clock.UtcNow; - - DateTimeOffset? absoluteExpiration = null; - if (entry.AbsoluteExpirationRelativeToNow.HasValue) - { - absoluteExpiration = utcNow + entry.AbsoluteExpirationRelativeToNow; - } - else if (entry.AbsoluteExpiration.HasValue) - { - absoluteExpiration = entry.AbsoluteExpiration; - } - - // Applying the option's absolute expiration only if it's not already smaller. - // This can be the case if a dependent cache entry has a smaller value, and - // it was set by cascading it to its parent. - if (absoluteExpiration.HasValue) - { - if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration.Value < entry.AbsoluteExpiration.Value) - { - entry.AbsoluteExpiration = absoluteExpiration; - } - } - - // Initialize the last access timestamp at the time the entry is added - entry.LastAccessed = utcNow; - - if (_entries.TryGetValue(entry.Key, out CacheEntry priorEntry)) - { - priorEntry.SetExpired(EvictionReason.Replaced); - } - - bool exceedsCapacity = UpdateCacheSizeExceedsCapacity(entry); - - if (!entry.CheckExpired(utcNow) && !exceedsCapacity) - { - bool entryAdded = false; - - if (priorEntry == null) - { - // Try to add the new entry if no previous entries exist. - entryAdded = _entries.TryAdd(entry.Key, entry); - } - else - { - // Try to update with the new entry if a previous entries exist. - entryAdded = _entries.TryUpdate(entry.Key, entry, priorEntry); - - if (entryAdded) - { - if (_options.SizeLimit.HasValue) - { - // The prior entry was removed, decrease the by the prior entry's size - Interlocked.Add(ref _cacheSize, -priorEntry.Size.Value); - } - } - else - { - // The update will fail if the previous entry was removed after retrival. - // Adding the new entry will succeed only if no entry has been added since. - // This guarantees removing an old entry does not prevent adding a new entry. - entryAdded = _entries.TryAdd(entry.Key, entry); - } - } - - if (entryAdded) - { - entry.AttachTokens(); - } - else - { - if (_options.SizeLimit.HasValue) - { - // Entry could not be added, reset cache size - Interlocked.Add(ref _cacheSize, -entry.Size.Value); - } - entry.SetExpired(EvictionReason.Replaced); - entry.InvokeEvictionCallbacks(); - } - - if (priorEntry != null) - { - priorEntry.InvokeEvictionCallbacks(); - } - } - else - { - if (exceedsCapacity) - { - // The entry was not added due to overcapacity - entry.SetExpired(EvictionReason.Capacity); - - TriggerOvercapacityCompaction(); - } - else - { - if (_options.SizeLimit.HasValue) - { - // Entry could not be added due to being expired, reset cache size - Interlocked.Add(ref _cacheSize, -entry.Size.Value); - } - } - - entry.InvokeEvictionCallbacks(); - if (priorEntry != null) - { - RemoveEntry(priorEntry); - } - } - - StartScanForExpiredItemsIfNeeded(utcNow); - } - - /// - public bool TryGetValue(object key, out object result) - { - ValidateCacheKey(key); - CheckDisposed(); - - DateTimeOffset utcNow = _options.Clock.UtcNow; - - if (_entries.TryGetValue(key, out CacheEntry entry)) - { - // Check if expired due to expiration tokens, timers, etc. and if so, remove it. - // Allow a stale Replaced value to be returned due to concurrent calls to SetExpired during SetEntry. - if (!entry.CheckExpired(utcNow) || entry.EvictionReason == EvictionReason.Replaced) - { - entry.LastAccessed = utcNow; - result = entry.Value; - - if (TrackLinkedCacheEntries && entry.CanPropagateOptions()) - { - // When this entry is retrieved in the scope of creating another entry, - // that entry needs a copy of these expiration tokens. - entry.PropagateOptions(CacheEntryHelper.Current); - } - - StartScanForExpiredItemsIfNeeded(utcNow); - - return true; - } - else - { - // TODO: For efficiency queue this up for batch removal - RemoveEntry(entry); - } - } - - StartScanForExpiredItemsIfNeeded(utcNow); - - result = null; - return false; - } - - /// - public void Remove(object key) - { - ValidateCacheKey(key); - - CheckDisposed(); - if (_entries.TryRemove(key, out CacheEntry entry)) - { - if (_options.SizeLimit.HasValue) - { - Interlocked.Add(ref _cacheSize, -entry.Size.Value); - } - - entry.SetExpired(EvictionReason.Removed); - entry.InvokeEvictionCallbacks(); - } - - StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow); - } - - private void RemoveEntry(CacheEntry entry) - { - if (EntriesCollection.Remove(new KeyValuePair(entry.Key, entry))) - { - if (_options.SizeLimit.HasValue) - { - Interlocked.Add(ref _cacheSize, -entry.Size.Value); - } - entry.InvokeEvictionCallbacks(); - } - } - - internal void EntryExpired(CacheEntry entry) - { - // TODO: For efficiency consider processing these expirations in batches. - RemoveEntry(entry); - StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow); - } - - // Called by multiple actions to see how long it's been since we last checked for expired items. - // If sufficient time has elapsed then a scan is initiated on a background task. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void StartScanForExpiredItemsIfNeeded(DateTimeOffset utcNow) - { - if (_options.ExpirationScanFrequency < utcNow - _lastExpirationScan) - { - ScheduleTask(utcNow); - } - - void ScheduleTask(DateTimeOffset utcNow) - { - _lastExpirationScan = utcNow; - Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this, - CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); - } - } - - private static void ScanForExpiredItems(MemoryCache cache) - { - DateTimeOffset now = cache._lastExpirationScan = cache._options.Clock.UtcNow; - - foreach (KeyValuePair item in cache._entries) - { - CacheEntry entry = item.Value; - - if (entry.CheckExpired(now)) - { - cache.RemoveEntry(entry); - } - } - } - - private bool UpdateCacheSizeExceedsCapacity(CacheEntry entry) - { - if (!_options.SizeLimit.HasValue) - { - return false; - } - - long newSize = 0L; - for (int i = 0; i < 100; i++) - { - long sizeRead = Interlocked.Read(ref _cacheSize); - newSize = sizeRead + entry.Size.Value; - - if (newSize < 0 || newSize > _options.SizeLimit) - { - // Overflow occurred, return true without updating the cache size - return true; - } - - if (sizeRead == Interlocked.CompareExchange(ref _cacheSize, newSize, sizeRead)) - { - return false; - } - } - - return true; - } - - private void TriggerOvercapacityCompaction() - { - _logger.LogDebug("Overcapacity compaction triggered"); - - // Spawn background thread for compaction - ThreadPool.QueueUserWorkItem(s => OvercapacityCompaction((MemoryCache)s), this); - } - - private static void OvercapacityCompaction(MemoryCache cache) - { - long currentSize = Interlocked.Read(ref cache._cacheSize); - - cache._logger.LogDebug($"Overcapacity compaction executing. Current size {currentSize}"); - - double? lowWatermark = cache._options.SizeLimit * (1 - cache._options.CompactionPercentage); - if (currentSize > lowWatermark) - { - cache.Compact(currentSize - (long)lowWatermark, entry => entry.Size.Value); - } - - cache._logger.LogDebug($"Overcapacity compaction executed. New size {Interlocked.Read(ref cache._cacheSize)}"); - } - - /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy: - /// 1. Remove all expired items. - /// 2. Bucket by CacheItemPriority. - /// 3. Least recently used objects. - /// ?. Items with the soonest absolute expiration. - /// ?. Items with the soonest sliding expiration. - /// ?. Larger objects - estimated by object graph size, inaccurate. - public void Compact(double percentage) - { - int removalCountTarget = (int)(_entries.Count * percentage); - Compact(removalCountTarget, _ => 1); - } - - private void Compact(long removalSizeTarget, Func computeEntrySize) - { - var entriesToRemove = new List(); - var lowPriEntries = new List(); - var normalPriEntries = new List(); - var highPriEntries = new List(); - long removedSize = 0; - - // Sort items by expired & priority status - DateTimeOffset now = _options.Clock.UtcNow; - foreach (KeyValuePair item in _entries) - { - CacheEntry entry = item.Value; - if (entry.CheckExpired(now)) - { - entriesToRemove.Add(entry); - removedSize += computeEntrySize(entry); - } - else - { - switch (entry.Priority) - { - case CacheItemPriority.Low: - lowPriEntries.Add(entry); - break; - case CacheItemPriority.Normal: - normalPriEntries.Add(entry); - break; - case CacheItemPriority.High: - highPriEntries.Add(entry); - break; - case CacheItemPriority.NeverRemove: - break; - default: - throw new NotSupportedException("Not implemented: " + entry.Priority); - } - } - } - - ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, lowPriEntries); - ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, normalPriEntries); - ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, highPriEntries); - - foreach (CacheEntry entry in entriesToRemove) - { - RemoveEntry(entry); - } - - // Policy: - // 1. Least recently used objects. - // ?. Items with the soonest absolute expiration. - // ?. Items with the soonest sliding expiration. - // ?. Larger objects - estimated by object graph size, inaccurate. - static void ExpirePriorityBucket(ref long removedSize, long removalSizeTarget, Func computeEntrySize, List entriesToRemove, List priorityEntries) - { - // Do we meet our quota by just removing expired entries? - if (removalSizeTarget <= removedSize) - { - // No-op, we've met quota - return; - } - - // Expire enough entries to reach our goal - // TODO: Refine policy - - // LRU - priorityEntries.Sort((e1, e2) => e1.LastAccessed.CompareTo(e2.LastAccessed)); - foreach (CacheEntry entry in priorityEntries) - { - entry.SetExpired(EvictionReason.Capacity); - entriesToRemove.Add(entry); - removedSize += computeEntrySize(entry); - - if (removalSizeTarget <= removedSize) - { - break; - } - } - } - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - GC.SuppressFinalize(this); - } - - _disposed = true; - } - } - - private void CheckDisposed() - { - if (_disposed) - { - Throw(); - } - - static void Throw() => throw new ObjectDisposedException(typeof(MemoryCache).FullName); - } - - private static void ValidateCacheKey(object key) - { - if (key == null) - { - Throw(); - } - - static void Throw() => throw new ArgumentNullException(nameof(key)); - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs b/frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs deleted file mode 100644 index d714c96df42..00000000000 --- a/frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Extensions.Internal; -using Microsoft.Extensions.Options; - -namespace appMpower.Memory -{ - public class MemoryCacheOptions : IOptions - { - private long? _sizeLimit; - private double _compactionPercentage = 0.05; - - public ISystemClock Clock { get; set; } - - /// - /// Gets or sets the minimum length of time between successive scans for expired items. - /// - public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Gets or sets the maximum size of the cache. - /// - public long? SizeLimit - { - get => _sizeLimit; - set - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative."); - } - - _sizeLimit = value; - } - } - - /// - /// Gets or sets the amount to compact the cache by when the maximum size is exceeded. - /// - public double CompactionPercentage - { - get => _compactionPercentage; - set - { - if (value < 0 || value > 1) - { - throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be between 0 and 1 inclusive."); - } - - _compactionPercentage = value; - } - } - - /// - /// Gets or sets whether to track linked entries. Disabled by default. - /// - /// Prior to .NET 7 this feature was always enabled. - public bool TrackLinkedCacheEntries { get; set; } - - MemoryCacheOptions IOptions.Value - { - get { return this; } - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs b/frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs deleted file mode 100644 index 3bc21736c08..00000000000 --- a/frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace PlatformBenchmarks -{ - public sealed class CacheKey : IEquatable - { - private readonly int _value; - - public CacheKey(int value) - => _value = value; - - public bool Equals(CacheKey key) - => key._value == _value; - - public override bool Equals(object obj) - => ReferenceEquals(obj, this); - - public override int GetHashCode() - => _value; - - public override string ToString() - => _value.ToString(); - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Microsoft/StringBuilderCache.cs b/frameworks/CSharp/appmpower/src/Microsoft/StringBuilderCache.cs deleted file mode 100644 index 6f0b20b7278..00000000000 --- a/frameworks/CSharp/appmpower/src/Microsoft/StringBuilderCache.cs +++ /dev/null @@ -1,59 +0,0 @@ -// 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; -using System.Text; - -namespace PlatformBenchmarks -{ - internal static class StringBuilderCache - { - private const int DefaultCapacity = 1386; - private const int MaxBuilderSize = DefaultCapacity * 3; - - [ThreadStatic] - private static StringBuilder t_cachedInstance; - - /// Get a StringBuilder for the specified capacity. - /// If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied. - public static StringBuilder Acquire(int capacity = DefaultCapacity) - { - if (capacity <= MaxBuilderSize) - { - StringBuilder sb = t_cachedInstance; - if (capacity < DefaultCapacity) - { - capacity = DefaultCapacity; - } - - if (sb != null) - { - // Avoid stringbuilder block fragmentation by getting a new StringBuilder - // when the requested size is larger than the current capacity - if (capacity <= sb.Capacity) - { - t_cachedInstance = null; - sb.Clear(); - return sb; - } - } - } - return new StringBuilder(capacity); - } - - public static void Release(StringBuilder sb) - { - if (sb.Capacity <= MaxBuilderSize) - { - t_cachedInstance = sb; - } - } - - public static string GetStringAndRelease(StringBuilder sb) - { - string result = sb.ToString(); - Release(sb); - return result; - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Program.cs b/frameworks/CSharp/appmpower/src/Program.cs deleted file mode 100644 index 6e748b08497..00000000000 --- a/frameworks/CSharp/appmpower/src/Program.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting.Server.Features; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; - -namespace appMpower -{ - class Program - { - static async Task Main(string[] args) - { - var socketTransportOptions = new SocketTransportOptions(); - var socketTransportFactory = new SocketTransportFactory(Options.Create(socketTransportOptions), NullLoggerFactory.Instance); - var kestrelServerOptions = new KestrelServerOptions(); - - kestrelServerOptions.Listen(IPAddress.Any, 8080); - kestrelServerOptions.AllowSynchronousIO = true; - kestrelServerOptions.AddServerHeader = false; - - using var kestrelServer = new KestrelServer(Options.Create(kestrelServerOptions), socketTransportFactory, NullLoggerFactory.Instance); - - await kestrelServer.StartAsync(new HttpApplication(), CancellationToken.None); - - Console.WriteLine("Listening on:"); - - foreach (var address in kestrelServer.Features.Get().Addresses) - { - Console.WriteLine(" - " + address); - } - - Console.WriteLine("Process CTRL+C to quit"); - var wh = new ManualResetEventSlim(); - Console.CancelKeyPress += (sender, e) => wh.Set(); - wh.Wait(); - } - } -} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Constants.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Constants.cs new file mode 100644 index 00000000000..c1678a71a62 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Constants.cs @@ -0,0 +1,10 @@ +using appMpower.Orm.Data; + +namespace appMpower.Orm +{ + public static class Constants + { + public static Dbms Dbms = Dbms.PostgreSQL; + public static DbProvider DbProvider = DbProvider.ODBC; + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Data/DbCommand.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbCommand.cs similarity index 54% rename from frameworks/CSharp/appmpower/src/Data/DbCommand.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbCommand.cs index 2086068c802..0dfe4581906 100644 --- a/frameworks/CSharp/appmpower/src/Data/DbCommand.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbCommand.cs @@ -1,56 +1,40 @@ using System.Data; -using System.Threading.Tasks; +using System.Data.Odbc; -namespace appMpower.Data +namespace appMpower.Orm.Data { public class DbCommand : IDbCommand { - private IDbCommand _dbCommand; + private OdbcCommand _odbcCommand; private DbConnection _dbConnection; public DbCommand(DbConnection dbConnection) { - _dbCommand = dbConnection.CreateCommand(); + _odbcCommand = (OdbcCommand)dbConnection.CreateCommand(); _dbConnection = dbConnection; } public DbCommand(string commandText, DbConnection dbConnection) { - dbConnection.GetCommand(commandText, CommandType.Text, this); + _odbcCommand = dbConnection.GetCommand(commandText, CommandType.Text); + _dbConnection = dbConnection; } public DbCommand(string commandText, CommandType commandType, DbConnection dbConnection) { - dbConnection.GetCommand(commandText, commandType, this); - } - - internal DbCommand(IDbCommand dbCommand, DbConnection dbConnection) - { - _dbCommand = dbCommand; - _dbConnection = dbConnection; + _odbcCommand = dbConnection.GetCommand(commandText, commandType); + _dbConnection = dbConnection; } internal IDbCommand Command { get { - return _dbCommand; + return _odbcCommand; } set { - _dbCommand = value; - } - } - - internal DbConnection DbConnection - { - get - { - return _dbConnection; - } - set - { - _dbConnection = value; + _odbcCommand = (OdbcCommand)value; } } @@ -58,11 +42,11 @@ public string CommandText { get { - return _dbCommand.CommandText; + return _odbcCommand.CommandText; } set { - _dbCommand.CommandText = value; + _odbcCommand.CommandText = value; } } @@ -70,22 +54,22 @@ public int CommandTimeout { get { - return _dbCommand.CommandTimeout; + return _odbcCommand.CommandTimeout; } set { - _dbCommand.CommandTimeout = value; + _odbcCommand.CommandTimeout = value; } } public CommandType CommandType { get { - return _dbCommand.CommandType; + return _odbcCommand.CommandType; } set { - _dbCommand.CommandType = value; + _odbcCommand.CommandType = value; } } @@ -94,11 +78,11 @@ public IDbConnection? Connection { get { - return _dbCommand.Connection; + return _odbcCommand.Connection; } set { - _dbCommand.Connection = (IDbConnection?)value; + _odbcCommand.Connection = (OdbcConnection?)value; } } #nullable disable @@ -107,7 +91,7 @@ public IDataParameterCollection Parameters { get { - return _dbCommand.Parameters; + return _odbcCommand.Parameters; } } @@ -116,11 +100,11 @@ public IDbTransaction? Transaction { get { - return _dbCommand.Transaction; + return _odbcCommand.Transaction; } set { - _dbCommand.Transaction = (IDbTransaction?)value; + _odbcCommand.Transaction = (OdbcTransaction?)value; } } #nullable disable @@ -129,21 +113,21 @@ public UpdateRowSource UpdatedRowSource { get { - return _dbCommand.UpdatedRowSource; + return _odbcCommand.UpdatedRowSource; } set { - _dbCommand.UpdatedRowSource = value; + _odbcCommand.UpdatedRowSource = value; } } public void Cancel() { - _dbCommand.Cancel(); + _odbcCommand.Cancel(); } public IDbDataParameter CreateParameter() { - return _dbCommand.CreateParameter(); + return _odbcCommand.CreateParameter(); } public IDbDataParameter CreateParameter(string name, object value) @@ -153,21 +137,21 @@ public IDbDataParameter CreateParameter(string name, object value) public IDbDataParameter CreateParameter(string name, DbType dbType, object value) { - IDbDataParameter dbDataParameter = null; + IDbDataParameter dbDataParameter; - if (this.Parameters.Contains(name)) + if (_odbcCommand.Parameters.Contains(name)) { - dbDataParameter = this.Parameters[name] as IDbDataParameter; + dbDataParameter = _odbcCommand.Parameters[name]; dbDataParameter.Value = value; } else { - dbDataParameter = _dbCommand.CreateParameter(); + dbDataParameter = _odbcCommand.CreateParameter(); dbDataParameter.ParameterName = name; dbDataParameter.DbType = dbType; dbDataParameter.Value = value; - this.Parameters.Add(dbDataParameter); + _odbcCommand.Parameters.Add(dbDataParameter); } return dbDataParameter; @@ -175,51 +159,44 @@ public IDbDataParameter CreateParameter(string name, DbType dbType, object value public int ExecuteNonQuery() { - return _dbCommand.ExecuteNonQuery(); + return _odbcCommand.ExecuteNonQuery(); } public IDataReader ExecuteReader() { - return _dbCommand.ExecuteReader(); + return _odbcCommand.ExecuteReader(); } public async Task ExecuteNonQueryAsync() { - return await (_dbCommand as System.Data.Common.DbCommand).ExecuteNonQueryAsync(); - } - - public async Task ExecuteReaderAsync(CommandBehavior behavior) - { - return await (_dbCommand as System.Data.Common.DbCommand).ExecuteReaderAsync(behavior); + return await _odbcCommand.ExecuteNonQueryAsync(); } public IDataReader ExecuteReader(CommandBehavior behavior) { - return _dbCommand.ExecuteReader(behavior); + return _odbcCommand.ExecuteReader(behavior); } -#nullable enable - public object? ExecuteScalar() + public async Task ExecuteReaderAsync(CommandBehavior behavior) { - return _dbCommand.ExecuteScalar(); + return await _odbcCommand.ExecuteReaderAsync(behavior); } -#nullable disable #nullable enable - public async Task ExecuteScalarAsync() + public object? ExecuteScalar() { - return await ((System.Data.Common.DbCommand)_dbCommand).ExecuteScalarAsync(); + return _odbcCommand.ExecuteScalar(); } #nullable disable public void Prepare() { - _dbCommand.Prepare(); + _odbcCommand.Prepare(); } public void Dispose() { - _dbConnection.ReleaseCommand(this); + _dbConnection.Release(_odbcCommand); } } } \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnection.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnection.cs new file mode 100644 index 00000000000..1a9c5ae0355 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnection.cs @@ -0,0 +1,174 @@ +using System.Collections.Concurrent; +using System.Data; +using System.Data.Odbc; + +namespace appMpower.Orm.Data +{ + public class DbConnection : IDbConnection + { + private string _connectionString; + private bool _keyed = false; + private int _number; + private OdbcConnection _odbcConnection; + private ConcurrentStack _odbcCommands = new(); + private Dictionary _keyedOdbcCommands; + + public DbConnection() + { + } + + public DbConnection(string connectionString, bool keyed = false) + { + _keyed = keyed; + _connectionString = connectionString; + GetConnection(); + } + + public IDbConnection Connection + { + get + { + return _odbcConnection; + } + set + { + _odbcConnection = (OdbcConnection)value; + } + } + + public string ConnectionString + { + get + { + return _odbcConnection.ConnectionString; + } + set + { + _connectionString = value; + GetConnection(); + } + } + + private void GetConnection() + { + if (_keyed) + { + (_number, _odbcConnection, _keyedOdbcCommands) = + DbConnectionsKeyed.GetConnectionBase(_connectionString).GetAwaiter().GetResult(); + } + else + { + (_number, _odbcConnection, _odbcCommands) = + DbConnections.GetConnectionBase(_connectionString).GetAwaiter().GetResult(); + } + } + + public int ConnectionTimeout + { + get + { + return _odbcConnection.ConnectionTimeout; + } + } + + public string Database + { + get + { + return _odbcConnection.Database; + } + } + + public ConnectionState State + { + get + { + if (_odbcConnection is null) return ConnectionState.Closed; + return _odbcConnection.State; + } + } + + public IDbTransaction BeginTransaction() + { + return _odbcConnection.BeginTransaction(); + } + + public IDbTransaction BeginTransaction(IsolationLevel il) + { + return _odbcConnection.BeginTransaction(il); + } + + public void ChangeDatabase(string databaseName) + { + _odbcConnection.ChangeDatabase(databaseName); + } + + public void Close() + { + _odbcConnection.Close(); + } + + public IDbCommand CreateCommand() + { + return _odbcConnection.CreateCommand(); + } + + public void Open() + { + if (_odbcConnection.State == ConnectionState.Closed) + { + _odbcConnection.Open(); + } + } + + public async Task OpenAsync() + { + if (_odbcConnection.State == ConnectionState.Closed) + { + await _odbcConnection.OpenAsync(); + } + } + + public void Dispose() + { + if (_keyed) + { + DbConnectionsKeyed.Release((Number: _number, OdbcConnection: _odbcConnection, KeyedOdbcCommands: _keyedOdbcCommands)); + } + else + { + DbConnections.Release((Number: _number, OdbcConnection: _odbcConnection, OdbcCommands: _odbcCommands)); + } + } + + internal OdbcCommand GetCommand(string commandText, CommandType commandType) + { + OdbcCommand odbcCommand; + + if (_odbcCommands.TryPop(out odbcCommand)) + { + if (commandText != odbcCommand.CommandText) + { + odbcCommand.CommandText = commandText; + odbcCommand.Parameters.Clear(); + } + + return odbcCommand; + } + else if (_keyed && _keyedOdbcCommands.TryGetValue(commandText, out odbcCommand)) return odbcCommand; + + odbcCommand = _odbcConnection.CreateCommand(); + odbcCommand.CommandText = commandText; + odbcCommand.CommandType = commandType; + odbcCommand.Prepare(); + + return odbcCommand; + } + + internal void Release(OdbcCommand odbcCommand) + { + if (_keyed) _keyedOdbcCommands.TryAdd(odbcCommand.CommandText, odbcCommand); + else _odbcCommands.Push(odbcCommand); + } + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnections.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnections.cs new file mode 100644 index 00000000000..b278e54fa57 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnections.cs @@ -0,0 +1,61 @@ +using System.Collections.Concurrent; +using System.Data.Odbc; + +namespace appMpower.Orm.Data +{ + internal static class DbConnections + { + private static bool _maxConnectionsCreated = false; + private static short _createdConnections = 0; + private static short _maxConnections = 500; + + private static ConcurrentStack<(int Number, OdbcConnection OdbcConnection, ConcurrentStack OdbcCommands)> _connectionsStack = new(); + private static ConcurrentQueue OdbcCommands)>> _waitingQueue = new(); + + internal static async Task<(int Number, OdbcConnection OdbcConnection, ConcurrentStack OdbcCommands)> GetConnectionBase(string connectionString) + { + (int Number, OdbcConnection OdbcConnection, ConcurrentStack OdbcCommands) dbConnectionBase; + + if (!_connectionsStack.TryPop(out dbConnectionBase)) + { + if (_maxConnectionsCreated) + { + dbConnectionBase = await GetDbConnectionBaseAsync(); + } + else + { + _createdConnections++; + dbConnectionBase = (Number: _maxConnections, OdbcConnection: new OdbcConnection(connectionString), OdbcCommands: new ConcurrentStack()); + + if (_createdConnections == _maxConnections) _maxConnectionsCreated = true; + + //Console.WriteLine("opened connection number: " + dbConnectionBase._number); + } + } + + return dbConnectionBase; + } + + internal static void Release((int Number, OdbcConnection OdbcConnection, ConcurrentStack OdbcCommands) dbConnectionBase) + { + TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, ConcurrentStack OdbcCommands)> taskCompletionSource; + + if (_waitingQueue.TryDequeue(out taskCompletionSource)) + { + taskCompletionSource.SetResult(dbConnectionBase); + } + else + { + _connectionsStack.Push(dbConnectionBase); + } + } + + private static Task<(int Number, OdbcConnection OdbcConnection, ConcurrentStack OdbcCommands)> GetDbConnectionBaseAsync() + { + var taskCompletionSource = new TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, ConcurrentStack OdbcCommands)>(TaskCreationOptions.RunContinuationsAsynchronously); + + _waitingQueue.Enqueue(taskCompletionSource); + return taskCompletionSource.Task; + } + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnectionsKeyed.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnectionsKeyed.cs new file mode 100644 index 00000000000..25cd4dcbab7 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnectionsKeyed.cs @@ -0,0 +1,61 @@ +using System.Collections.Concurrent; +using System.Data.Odbc; + +namespace appMpower.Orm.Data +{ + internal static class DbConnectionsKeyed + { + private static bool _maxConnectionsCreated = false; + private static short _createdConnections = 0; + private static short _maxConnections = 500; + + private static ConcurrentStack<(int Number, OdbcConnection OdbcConnection, Dictionary)> _connectionsStack = new(); + private static ConcurrentQueue)>> _waitingQueue = new(); + + internal static async Task<(int Number, OdbcConnection OdbcConnection, Dictionary KeyedOdbcCommands)> GetConnectionBase(string connectionString) + { + (int Number, OdbcConnection OdbcConnection, Dictionary KeyedOdbcCommands) dbConnectionBase; + + if (!_connectionsStack.TryPop(out dbConnectionBase)) + { + if (_maxConnectionsCreated) + { + dbConnectionBase = await GetDbConnectionBaseAsync(); + } + else + { + _createdConnections++; + dbConnectionBase = (Number: _maxConnections, OdbcConnection: new OdbcConnection(connectionString), KeyedOdbcCommands: new Dictionary()); + + if (_createdConnections == _maxConnections) _maxConnectionsCreated = true; + + //Console.WriteLine("opened connection number: " + dbConnectionBase._number); + } + } + + return dbConnectionBase; + } + + internal static void Release((int Number, OdbcConnection OdbcConnection, Dictionary KeyedOdbcCommands) dbConnectionBase) + { + TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, Dictionary)> taskCompletionSource; + + if (_waitingQueue.TryDequeue(out taskCompletionSource)) + { + taskCompletionSource.SetResult(dbConnectionBase); + } + else + { + _connectionsStack.Push(dbConnectionBase); + } + } + + private static Task<(int Number, OdbcConnection OdbcConnection, Dictionary)> GetDbConnectionBaseAsync() + { + var taskCompletionSource = new TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, Dictionary)>(TaskCreationOptions.RunContinuationsAsynchronously); + + _waitingQueue.Enqueue(taskCompletionSource); + return taskCompletionSource.Task; + } + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProvider.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProvider.cs new file mode 100644 index 00000000000..bf60750b52d --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProvider.cs @@ -0,0 +1,8 @@ +namespace appMpower.Orm.Data +{ + public enum DbProvider + { + ADO = 0, + ODBC = 1, + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProviderFactory.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProviderFactory.cs new file mode 100644 index 00000000000..1e72d164a25 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProviderFactory.cs @@ -0,0 +1,21 @@ +using System.Data; + +namespace appMpower.Orm.Data +{ + public static class DbProviderFactory + { + public static string ConnectionString; + + public static void SetConnectionString() + { + if (Constants.Dbms == Dbms.MySQL) + { + ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1;sslmode=DISABLED;CharSet=utf8;"; + } + else + { + ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false;sslmode=disable"; + } + } + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/Dbms.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/Dbms.cs new file mode 100644 index 00000000000..8c8bbeb67e6 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Data/Dbms.cs @@ -0,0 +1,9 @@ +namespace appMpower.Orm.Data +{ + public enum Dbms + { + MySQL = 0, + PostgreSQL = 1, + SQLServer = 2, + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/DotnetMethods.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/DotnetMethods.cs new file mode 100644 index 00000000000..5ea4ce43fa2 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/DotnetMethods.cs @@ -0,0 +1,66 @@ +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json; +using appMpower.Orm.Data; +using appMpower.Orm.Objects; +using appMpower.Orm.Serializers; + +namespace appMpower.Orm; + +//These methods are for test purposes only; not used in actual execution +public static class DotnetMethods +{ + private static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions + { + Indented = false, + SkipValidation = true + }; + + private readonly static WorldSerializer _worldSerializer = new WorldSerializer(); + private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer(); + + public static byte[] Db() + { + var world = RawDb.LoadSingleQueryRow().GetAwaiter().GetResult(); + + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _worldSerializer.Serialize(utf8JsonWriter, world); + + return memoryStream.ToArray(); + } + + public static byte[] Query(int queries) + { + World[] worlds = RawDb.ReadMultipleRows(queries).GetAwaiter().GetResult(); + + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _worldsSerializer.Serialize(utf8JsonWriter, worlds); + + return memoryStream.ToArray(); + } + + public static byte[] Updates(int count) + { + World[] worlds = RawDb.LoadMultipleUpdatesRows(count).GetAwaiter().GetResult(); + + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _worldsSerializer.Serialize(utf8JsonWriter, worlds); + + return memoryStream.ToArray(); + } + + public static byte[] Fortunes() + { + List fortunes = RawDb.LoadFortunesRows().GetAwaiter().GetResult(); + string fortunesView = FortunesView.Render(fortunes); + byte[] byteArray = Encoding.UTF8.GetBytes(fortunesView); + + return byteArray.ToArray(); + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/FortunesView.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/FortunesView.cs similarity index 54% rename from frameworks/CSharp/appmpower/src/FortunesView.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/FortunesView.cs index 8f56ddde4d5..2c11f6473c6 100644 --- a/frameworks/CSharp/appmpower/src/FortunesView.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/FortunesView.cs @@ -1,33 +1,23 @@ using System.Collections.Generic; -using System.IO.Pipelines; using System.Globalization; using System.Text; using System.Threading.Tasks; using System.Web; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; using PlatformBenchmarks; +using appMpower.Orm.Objects; -namespace appMpower +namespace appMpower.Orm { public static class FortunesView { - private readonly static KeyValuePair _headerServer = - new KeyValuePair("Server", "k"); - private readonly static KeyValuePair _headerContentType = - new KeyValuePair("Content-Type", "text/html; charset=UTF-8"); - public static char[] _fortunesTableStart = "Fortunes".ToCharArray(); public static char[] _fortunesRowStart = "".ToCharArray(); public static char[] _fortunesTableEnd = "
idmessage
".ToCharArray(); public static char[] _fortunesColumn = "".ToCharArray(); public static char[] _fortunesRowEnd = "
".ToCharArray(); - public static async Task Render(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, List fortunes) + public static string Render(List fortunes) { - headerDictionary.Add(_headerServer); - headerDictionary.Add(_headerContentType); - var writer = StringBuilderCache.Acquire(); writer.Append(_fortunesTableStart); @@ -39,10 +29,7 @@ public static async Task Render(IHeaderDictionary headerDictionary, PipeWriter p writer.Append(_fortunesTableEnd); - headerDictionary.Add(new KeyValuePair("Content-Length", (writer.Length + 32).ToString())); - - await pipeWriter.WriteAsync(Encoding.UTF8.GetBytes(StringBuilderCache.GetStringAndRelease(writer))); - pipeWriter.Complete(); + return StringBuilderCache.GetStringAndRelease(writer); } } } \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/Microsoft/BatchUpdateString.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/BatchUpdateString.cs similarity index 54% rename from frameworks/CSharp/appmpower/src/Microsoft/BatchUpdateString.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/BatchUpdateString.cs index 0f3e501c485..a5887f50142 100644 --- a/frameworks/CSharp/appmpower/src/Microsoft/BatchUpdateString.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/BatchUpdateString.cs @@ -1,7 +1,8 @@ // 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.Linq; +using appMpower.Orm; +using appMpower.Orm.Data; namespace PlatformBenchmarks { @@ -33,51 +34,32 @@ public static string Query(int batchSize) //sb.Append("(?::int,?::int)) AS temp(id, randomNumber) WHERE temp.id = world.id"); */ -#if MYSQL - for (int i = 0; i < batchSize; i++) + if (Constants.Dbms == Dbms.MySQL) { - sb.Append("UPDATE world SET randomNumber=? WHERE id=?;"); + for (int i = 0; i < batchSize; i++) + { + sb.Append("UPDATE world SET randomNumber=? WHERE id=?;"); + } } -#elif ADO - /* - sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES "); - Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@i{i}, @r{i}), ")); - sb.Append($"(@i{lastIndex}, @r{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id"); - */ - - sb.Append("UPDATE world SET randomNumber=CASE id "); - - for (int i = 0; i < batchSize; i++) - { - sb.Append("WHEN @i" + i + " THEN @r" + i + " "); - } - - sb.Append("ELSE randomnumber END WHERE id IN("); - - for (int i = 0; i < lastIndex; i++) + else { - sb.Append("@j" + i + ","); - } + sb.Append("UPDATE world SET randomNumber=CASE id "); - sb.Append("@j" + lastIndex + ")"); -#else - sb.Append("UPDATE world SET randomNumber=CASE id "); + for (int i = 0; i < batchSize; i++) + { + sb.Append("WHEN ? THEN ? "); + } - for (int i = 0; i < batchSize; i++) - { - sb.Append("WHEN ? THEN ? "); - } + sb.Append("ELSE randomnumber END WHERE id IN("); - sb.Append("ELSE randomnumber END WHERE id IN("); + for (int i = 0; i < lastIndex; i++) + { + sb.Append("?,"); + } - for (int i = 0; i < lastIndex; i++) - { - sb.Append("?,"); + sb.Append("?)"); } - sb.Append("?)"); -#endif - return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb); } } diff --git a/frameworks/CSharp/appmpower/src/Microsoft/ConcurrentRandom.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/ConcurrentRandom.cs similarity index 95% rename from frameworks/CSharp/appmpower/src/Microsoft/ConcurrentRandom.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/ConcurrentRandom.cs index b5a74d48884..374c9f4e1bb 100644 --- a/frameworks/CSharp/appmpower/src/Microsoft/ConcurrentRandom.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/ConcurrentRandom.cs @@ -1,9 +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. -using System; using System.Runtime.CompilerServices; -using System.Threading; namespace PlatformBenchmarks { diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/StringBuilderCache.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/StringBuilderCache.cs new file mode 100644 index 00000000000..73b890db750 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/StringBuilderCache.cs @@ -0,0 +1,54 @@ +// 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; + +namespace PlatformBenchmarks; + +internal static class StringBuilderCache +{ + private const int DefaultCapacity = 1386; + private const int MaxBuilderSize = DefaultCapacity * 3; + [ThreadStatic] + private static StringBuilder t_cachedInstance; + + public static StringBuilder Acquire(int capacity = DefaultCapacity) + { + if (capacity <= MaxBuilderSize) + { + StringBuilder sb = t_cachedInstance; + + if (capacity < DefaultCapacity) + { + capacity = DefaultCapacity; + } + + if (sb != null) + { + if (capacity <= sb.Capacity) + { + t_cachedInstance = null; + sb.Clear(); + return sb; + } + } + } + + return new StringBuilder(capacity); + } + + public static void Release(StringBuilder sb) + { + if (sb.Capacity <= MaxBuilderSize) + { + t_cachedInstance = sb; + } + } + + public static string GetStringAndRelease(StringBuilder sb) + { + string result = sb.ToString(); + Release(sb); + return result; + } +} diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs new file mode 100644 index 00000000000..64337e6269e --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs @@ -0,0 +1,144 @@ +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json; +using appMpower.Orm.Data; +using appMpower.Orm.Objects; +using appMpower.Orm.Serializers; + +namespace appMpower.Orm; + +public static class NativeMethods +{ + private static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions + { + Indented = false, + SkipValidation = true + }; + + private readonly static WorldSerializer _worldSerializer = new WorldSerializer(); + private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer(); + + [UnmanagedCallersOnly(EntryPoint = "Dbms")] + public static void Dbms(int dbms) + { + Constants.Dbms = (Dbms)dbms; + DbProviderFactory.SetConnectionString(); + } + + [UnmanagedCallersOnly(EntryPoint = "DbProvider")] + public static void DbProvider(int dbProvider) + { + Constants.DbProvider = (DbProvider)dbProvider; + DbProviderFactory.SetConnectionString(); + } + + [UnmanagedCallersOnly(EntryPoint = "FreeHandlePointer")] + public static void FreeHandlePointer(IntPtr handlePointer) + { + GCHandle handle = GCHandle.FromIntPtr(handlePointer); + handle.Free(); + } + + [UnmanagedCallersOnly(EntryPoint = "Db")] + public static unsafe IntPtr Db(int* length, IntPtr* handlePointer) + { + var world = RawDb.LoadSingleQueryRow().GetAwaiter().GetResult(); + + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _worldSerializer.Serialize(utf8JsonWriter, world); + + *length = (int)utf8JsonWriter.BytesCommitted; + byte[] byteArray = memoryStream.ToArray(); + + GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned); + // return the managed and byteArrayPointer pointer + IntPtr byteArrayPointer = handle.AddrOfPinnedObject(); + *handlePointer = GCHandle.ToIntPtr(handle); + + return byteArrayPointer; + /* + fixed(byte* b = memoryStream.ToArray()) + { + return b; + } + */ + } + + [UnmanagedCallersOnly(EntryPoint = "Fortunes")] + public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer) + { + List fortunes = RawDb.LoadFortunesRows().GetAwaiter().GetResult(); + string fortunesView = FortunesView.Render(fortunes); + byte[] byteArray = Encoding.UTF8.GetBytes(fortunesView); + + *length = byteArray.Length; + + 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) + { + World[] worlds = RawDb.ReadMultipleRows(queries).GetAwaiter().GetResult(); + + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _worldsSerializer.Serialize(utf8JsonWriter, worlds); + + *length = (int)utf8JsonWriter.BytesCommitted; + byte[] byteArray = memoryStream.ToArray(); + + GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned); + IntPtr byteArrayPointer = handle.AddrOfPinnedObject(); + *handlePointer = GCHandle.ToIntPtr(handle); + + return byteArrayPointer; + } + + [UnmanagedCallersOnly(EntryPoint = "Updates")] + public static unsafe IntPtr Updates(int count, int* length, IntPtr* handlePointer) + { + World[] worlds = RawDb.LoadMultipleUpdatesRows(count).GetAwaiter().GetResult(); + + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _worldsSerializer.Serialize(utf8JsonWriter, worlds); + + *length = (int)utf8JsonWriter.BytesCommitted; + byte[] byteArray = memoryStream.ToArray(); + + GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned); + IntPtr byteArrayPointer = handle.AddrOfPinnedObject(); + *handlePointer = GCHandle.ToIntPtr(handle); + + return byteArrayPointer; + } + + [UnmanagedCallersOnly(EntryPoint = "DbById")] + public static unsafe IntPtr DbById(int id, int* length, IntPtr* handlePointer) + { + var world = RawDb.LoadSingleQueryRowById(id).GetAwaiter().GetResult(); + + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _worldSerializer.Serialize(utf8JsonWriter, world); + + *length = (int)utf8JsonWriter.BytesCommitted; + byte[] byteArray = memoryStream.ToArray(); + + GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned); + IntPtr byteArrayPointer = handle.AddrOfPinnedObject(); + *handlePointer = GCHandle.ToIntPtr(handle); + + return byteArrayPointer; + } +} diff --git a/frameworks/CSharp/appmpower/src/CachedWorld.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/CachedWorld.cs similarity index 88% rename from frameworks/CSharp/appmpower/src/CachedWorld.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/CachedWorld.cs index 857b8283c73..7ef3f074167 100644 --- a/frameworks/CSharp/appmpower/src/CachedWorld.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/CachedWorld.cs @@ -1,4 +1,4 @@ -namespace appMpower +namespace appMpower.Orm.Objects { public struct CachedWorld { diff --git a/frameworks/CSharp/appmpower/src/Fortune.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/Fortune.cs similarity index 93% rename from frameworks/CSharp/appmpower/src/Fortune.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/Fortune.cs index 4d1b99661ca..c6eb823c9fa 100644 --- a/frameworks/CSharp/appmpower/src/Fortune.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/Fortune.cs @@ -1,6 +1,4 @@ -using System; - -namespace appMpower +namespace appMpower.Orm.Objects { public readonly struct Fortune : IComparable, IComparable { diff --git a/frameworks/CSharp/appmpower/src/World.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/World.cs similarity index 78% rename from frameworks/CSharp/appmpower/src/World.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/World.cs index cfec08b1f9c..4e10e128998 100644 --- a/frameworks/CSharp/appmpower/src/World.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/World.cs @@ -1,4 +1,4 @@ -namespace appMpower +namespace appMpower.Orm.Objects { public struct World { diff --git a/frameworks/CSharp/appmpower/src/RawDb.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/RawDb.cs similarity index 55% rename from frameworks/CSharp/appmpower/src/RawDb.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/RawDb.cs index 8813ce6dfcc..fec986f8755 100644 --- a/frameworks/CSharp/appmpower/src/RawDb.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/RawDb.cs @@ -1,34 +1,18 @@ -using System; -using System.Collections.Generic; using System.Data; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Caching.Memory; -using appMpower.Data; +using appMpower.Orm.Data; +using appMpower.Orm.Objects; using PlatformBenchmarks; -namespace appMpower +namespace appMpower.Orm { public static class RawDb { private const int MaxBatch = 500; -#if ADO - private static ConcurrentRandom _random = new ConcurrentRandom(); -#else private static Random _random = new Random(); -#endif private static string[] _queriesMultipleRows = new string[MaxBatch + 1]; - private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray(); - - private static readonly appMpower.Memory.MemoryCache _cache = new appMpower.Memory.MemoryCache( - new appMpower.Memory.MemoryCacheOptions() - { - ExpirationScanFrequency = TimeSpan.FromMinutes(60) - }); - public static async Task LoadSingleQueryRow() { using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString); @@ -38,7 +22,22 @@ public static async Task LoadSingleQueryRow() using (dbCommand) { - var world = await ReadSingleRow(dbCommand); + World world = await ReadSingleRow(dbCommand); + + return world; + } + } + + public static async Task LoadSingleQueryRowById(int id) + { + using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString); + await pooledConnection.OpenAsync(); + + var (dbCommand, _) = CreateReadCommandById(pooledConnection, id); + + using (dbCommand) + { + World world = await ReadSingleRow(dbCommand); return world; } @@ -76,21 +75,17 @@ public static async Task> LoadFortunesRows() using (dbCommand) { - var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleResult & CommandBehavior.SequentialAccess); + IDataReader dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleResult & CommandBehavior.SequentialAccess); while (dataReader.Read()) { fortunes.Add(new Fortune ( - id: dataReader.GetInt32(0), -#if MYSQL - //MariaDB ODBC connector does not correctly support Japanese characters in combination with default ADO.NET; - //as a solution we custom read this string - message: ReadColumn(dataReader, 1) -#else - message: dataReader.GetString(1) -#endif - )); + id: dataReader.GetInt32(0), + //MariaDB ODBC connector does not correctly support Japanese characters in combination with default ADO.NET; + //as a solution we custom read this string + message: (Constants.Dbms == Dbms.MySQL ? ReadColumn(dataReader, 1) : dataReader.GetString(1)) + )); } dataReader.Close(); @@ -106,7 +101,7 @@ public static async Task LoadMultipleUpdatesRows(int count) { var worlds = new World[count]; - using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString); + using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString, true); await pooledConnection.OpenAsync(); var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection); @@ -120,14 +115,10 @@ public static async Task LoadMultipleUpdatesRows(int count) } } - using var updateCommand = new DbCommand(PlatformBenchmarks.BatchUpdateString.Query(count), pooledConnection); + using var updateCommand = new DbCommand(BatchUpdateString.Query(count), pooledConnection); - var ids = PlatformBenchmarks.BatchUpdateString.Ids; - var randoms = PlatformBenchmarks.BatchUpdateString.Randoms; - -#if !MYSQL - var jds = PlatformBenchmarks.BatchUpdateString.Jds; -#endif + var ids = BatchUpdateString.Ids; + var randoms = BatchUpdateString.Randoms; for (int i = 0; i < count; i++) { @@ -139,30 +130,36 @@ public static async Task LoadMultipleUpdatesRows(int count) worlds[i].RandomNumber = randomNumber; } -#if !MYSQL - for (int i = 0; i < count; i++) + if (Constants.Dbms != Dbms.MySQL) { - updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id); + var jds = BatchUpdateString.Jds; + + for (int i = 0; i < count; i++) + { + updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id); + } } -#endif - await updateCommand.ExecuteNonQueryAsync(); + updateCommand.ExecuteNonQuery(); return worlds; } - private static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommand(DbConnection pooledConnection) + internal static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommand(DbConnection pooledConnection) { -#if ADO - var dbCommand = new DbCommand("SELECT * FROM world WHERE id=@Id", pooledConnection); -#else - var dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection); -#endif + DbCommand dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection); return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, _random.Next(1, 10001))); } - private static async Task ReadSingleRow(DbCommand dbCommand) + internal static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommandById(DbConnection pooledConnection, int id) + { + DbCommand dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection); + + return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, id)); + } + + internal static async Task ReadSingleRow(DbCommand dbCommand) { var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow & CommandBehavior.SequentialAccess); @@ -182,7 +179,7 @@ private static async Task ReadSingleRow(DbCommand dbCommand) public static async Task ReadMultipleRows(int count) { int j = 0; - var ids = PlatformBenchmarks.BatchUpdateString.Ids; + var ids = BatchUpdateString.Ids; var worlds = new World[count]; string queryString; @@ -192,14 +189,14 @@ public static async Task ReadMultipleRows(int count) } else { - var stringBuilder = PlatformBenchmarks.StringBuilderCache.Acquire(); + var stringBuilder = StringBuilderCache.Acquire(); for (int i = 0; i < count; i++) { stringBuilder.Append("SELECT * FROM world WHERE id=?;"); } - queryString = _queriesMultipleRows[count] = PlatformBenchmarks.StringBuilderCache.GetStringAndRelease(stringBuilder); + queryString = _queriesMultipleRows[count] = StringBuilderCache.GetStringAndRelease(stringBuilder); } using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString); @@ -249,83 +246,5 @@ public static string ReadColumn(IDataReader dataReader, int column) return System.Text.Encoding.Default.GetString(values); } - - public static async Task PopulateCache() - { - using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString); - await pooledConnection.OpenAsync(); - - var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection); - - using (dbCommand) - { - var cacheKeys = _cacheKeys; - var cache = _cache; - - for (var i = 1; i < 10001; i++) - { - dbDataParameter.Value = i; - cache.Set(cacheKeys[i], await ReadSingleRow(dbCommand)); - } - } - } - - public static Task LoadCachedQueries(int count) - { - var result = new CachedWorld[count]; - var cacheKeys = _cacheKeys; - var cache = _cache; - var random = _random; - - for (var i = 0; i < result.Length; i++) - { - var id = random.Next(1, 10001); - var key = cacheKeys[id]; - - if (cache.TryGetValue(key, out object cached)) - { - result[i] = (CachedWorld)cached; - } - else - { - //return LoadUncachedQueries(id, i, count, this, result); - return LoadUncachedQueries(id, i, count, result); - } - } - - return Task.FromResult(result); - } - - static async Task LoadUncachedQueries(int id, int i, int count, CachedWorld[] result) - { - using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString); - await pooledConnection.OpenAsync(); - - var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection); - - using (dbCommand) - { - Func> create = async (entry) => - { - return await ReadSingleRow(dbCommand); - }; - - var cacheKeys = _cacheKeys; - var key = cacheKeys[id]; - - dbDataParameter.Value = id; - - for (; i < result.Length; i++) - { - result[i] = await _cache.GetOrCreateAsync(key, create); - - id = _random.Next(1, 10001); - dbDataParameter.Value = id; - key = cacheKeys[id]; - } - } - - return result; - } } } \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/IJsonSerializer.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/IJsonSerializer.cs new file mode 100644 index 00000000000..8986ee387ee --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/IJsonSerializer.cs @@ -0,0 +1,9 @@ +using System.Text.Json; + +namespace appMpower.Orm.Serializers +{ + public interface IJsonSerializer + { + public void Serialize(Utf8JsonWriter utf8JsonWriter, T t); + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/WorldSerializer.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldSerializer.cs similarity index 68% rename from frameworks/CSharp/appmpower/src/WorldSerializer.cs rename to frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldSerializer.cs index 6150c5560b7..1e078da5957 100644 --- a/frameworks/CSharp/appmpower/src/WorldSerializer.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldSerializer.cs @@ -1,8 +1,9 @@ using System.Text.Json; +using appMpower.Orm.Objects; -namespace appMpower +namespace appMpower.Orm.Serializers { - public class WorldSerializer : Kestrel.IJsonSerializer + public class WorldSerializer : IJsonSerializer { public void Serialize(Utf8JsonWriter utf8JsonWriter, World world) { @@ -10,6 +11,7 @@ public void Serialize(Utf8JsonWriter utf8JsonWriter, World world) utf8JsonWriter.WriteNumber("id", world.Id); utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber); utf8JsonWriter.WriteEndObject(); + utf8JsonWriter.Flush(); } } } diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldsSerizalizer.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldsSerizalizer.cs new file mode 100644 index 00000000000..223af9bd702 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldsSerizalizer.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using appMpower.Orm.Objects; + +namespace appMpower.Orm.Serializers +{ + public class WorldsSerializer : IJsonSerializer + { + public void Serialize(Utf8JsonWriter utf8JsonWriter, World[] worlds) + { + utf8JsonWriter.WriteStartArray(); + + foreach (World world in worlds) + { + utf8JsonWriter.WriteStartObject(); + utf8JsonWriter.WriteNumber("id", world.Id); + utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber); + utf8JsonWriter.WriteEndObject(); + } + + utf8JsonWriter.WriteEndArray(); + utf8JsonWriter.Flush(); + } + } +} diff --git a/frameworks/CSharp/appmpower/src/appMpower.csproj b/frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj similarity index 52% rename from frameworks/CSharp/appmpower/src/appMpower.csproj rename to frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj index 579c54883b5..dd9c929f19f 100644 --- a/frameworks/CSharp/appmpower/src/appMpower.csproj +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj @@ -1,55 +1,42 @@ - - - - net8.0 - Exe - true - - - - - true - true - true - false - Speed - none - false - - - true - true - true - false - - - true - - true - - - - - true - - - true - false - false - - - - - - - - - - $(DefineConstants);POSTGRESQL - $(DefineConstants);ODBC - $(DefineConstants);ADO - - - \ No newline at end of file + + + + net8.0 + enable + + true + true + true + + linux-x64 + + + + + true + false + Size + none + false + + true + true + true + false + + true + + true + + true + + true + false + false + + + + + + + diff --git a/frameworks/CSharp/appmpower/src/appMpower/JsonMessage.cs b/frameworks/CSharp/appmpower/src/appMpower/JsonMessage.cs new file mode 100644 index 00000000000..ab1685ab8ad --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/JsonMessage.cs @@ -0,0 +1,7 @@ +namespace appMpower +{ + public struct JsonMessage + { + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/CachingMiddelware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/CachingMiddelware.cs new file mode 100644 index 00000000000..0719f19c5ad --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/CachingMiddelware.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace appMpower; + +public class CachingMiddleware +{ + private static readonly Dictionary _cache = new(); + private static readonly Random _random = new(); + + private static readonly byte[] _startBytes = Encoding.UTF8.GetBytes("["); + private static readonly byte[] _endBytes = Encoding.UTF8.GetBytes("]"); + private static readonly byte[] _comma = Encoding.UTF8.GetBytes(","); + + + private readonly static KeyValuePair _headerServer = + new KeyValuePair("Server", new StringValues("k")); + private readonly static KeyValuePair _headerContentType = + new KeyValuePair("Content-Type", new StringValues("application/json")); + + private readonly RequestDelegate _next; + + public CachingMiddleware(RequestDelegate next) + { + _next = next; + } + + public unsafe Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/cached-worlds", StringComparison.Ordinal)) + { + int payloadLength; + IntPtr handlePointer; + IntPtr bytePointer; + byte[] json; + + if (_cache.Count == 0) + { + for (int i = 1; i < 10001; i++) + { + bytePointer = NativeMethods.DbById(i, out payloadLength, out handlePointer); + json = new byte[payloadLength]; + Marshal.Copy(bytePointer, json, 0, payloadLength); + NativeMethods.FreeHandlePointer(handlePointer); + _cache.Add(i, json); + } + } + + var queryString = httpContext.Request.QueryString.ToString(); + int queries; + Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out queries); + queries = queries > 500 ? 500 : (queries > 0 ? queries : 1); + + var response = httpContext.Response; + response.Headers.Add(_headerServer); + response.Headers.Add(_headerContentType); + + int queriesLength = 0; + int[] keys = new int[queries]; + + for (int i = 0; i < queries; i++) + { + keys[i] = _random.Next(1, 10001); + + if (!_cache.TryGetValue(keys[i], out json)) + { + bytePointer = NativeMethods.DbById(keys[i], out payloadLength, out handlePointer); + json = new byte[payloadLength]; + Marshal.Copy(bytePointer, json, 0, payloadLength); + NativeMethods.FreeHandlePointer(handlePointer); + _cache.Add(keys[i], json); + } + + queriesLength += json.Length; + } + + byte[] result = new byte[_startBytes.Length + _endBytes.Length + (_comma.Length * queries - 1) + queriesLength]; + int position = 0; + + Buffer.BlockCopy(_startBytes, 0, result, position, _startBytes.Length); + position += _startBytes.Length; + + for (int i = 0; i < queries; i++) + { + json = _cache[keys[i]]; + Buffer.BlockCopy(json, 0, result, position, json.Length); + position += json.Length; + + if (i < queries - 1) + { + Buffer.BlockCopy(_comma, 0, result, position, _comma.Length); + position += _comma.Length; + } + } + + Buffer.BlockCopy(_endBytes, 0, result, position, _endBytes.Length); + + response.Headers.Add( + new KeyValuePair("Content-Length", result.Length.ToString())); + + return response.Body.WriteAsync(result, 0, result.Length); + } + + return _next(httpContext); + } +} + +public static class CachingMiddlewareExtensions +{ + public static IApplicationBuilder UseCaching(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs new file mode 100644 index 00000000000..2acf9af1716 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace appMpower; + +public class FortunesMiddleware +{ + 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 readonly RequestDelegate _next; + + public FortunesMiddleware(RequestDelegate next) + { + _next = next; + } + + public unsafe Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/fortunes", StringComparison.Ordinal)) + { + var response = httpContext.Response; + response.Headers.Add(_headerServer); + response.Headers.Add(_headerContentType); + + 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); + + response.Headers.Add( + new KeyValuePair("Content-Length", payloadLength.ToString())); + + return response.Body.WriteAsync(json, 0, payloadLength); + } + + return _next(httpContext); + } +} + +public static class FortunesMiddlewareExtensions +{ + public static IApplicationBuilder UseFortunes(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/JsonMiddleware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/JsonMiddleware.cs new file mode 100644 index 00000000000..2e90051ff2f --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/JsonMiddleware.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using appMpower.Serializers; + +namespace appMpower; + +public class JsonMiddleware +{ + private readonly static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions + { + Indented = false, + SkipValidation = true + }; + + private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer(); + + private readonly static KeyValuePair _headerServer = + new KeyValuePair("Server", new StringValues("k")); + private readonly static KeyValuePair _headerContentType = + new KeyValuePair("Content-Type", new StringValues("application/json")); + + private readonly RequestDelegate _nextStage; + + public JsonMiddleware(RequestDelegate nextStage) + { + _nextStage = nextStage; + } + + public unsafe Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/json", StringComparison.Ordinal)) + { + var response = httpContext.Response; + response.Headers.Add(_headerServer); + response.Headers.Add(_headerContentType); + + using var utf8JsonWriter = new Utf8JsonWriter(httpContext.Response.Body, _jsonWriterOptions); + + _jsonMessageSerializer.Serialize(utf8JsonWriter, new JsonMessage { Message = "Hello, World!" }); + + response.Headers.Add( + new KeyValuePair("Content-Length", utf8JsonWriter.BytesPending.ToString())); + + utf8JsonWriter.Flush(); + + return Task.CompletedTask; + } + + return _nextStage(httpContext); + } +} + +public static class JsonMiddlewareExtensions +{ + public static IApplicationBuilder UseJson(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleQueriesMiddleware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleQueriesMiddleware.cs new file mode 100644 index 00000000000..63964f0b90c --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleQueriesMiddleware.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace appMpower; + +public class MultipleQueriesMiddleware +{ + private readonly static KeyValuePair _headerServer = + new KeyValuePair("Server", new StringValues("k")); + private readonly static KeyValuePair _headerContentType = + new KeyValuePair("Content-Type", new StringValues("application/json")); + + private readonly RequestDelegate _next; + + public MultipleQueriesMiddleware(RequestDelegate next) + { + _next = next; + } + + public unsafe Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/queries", StringComparison.Ordinal)) + { + var queryString = httpContext.Request.QueryString.ToString(); + int queries; + Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out queries); + queries = queries > 500 ? 500 : (queries > 0 ? queries : 1); + + var response = httpContext.Response; + response.Headers.Add(_headerServer); + response.Headers.Add(_headerContentType); + + int payloadLength; + IntPtr handlePointer; + + IntPtr bytePointer = NativeMethods.Query(queries, out payloadLength, out handlePointer); + byte[] json = new byte[payloadLength]; + Marshal.Copy(bytePointer, json, 0, payloadLength); + NativeMethods.FreeHandlePointer(handlePointer); + + response.Headers.Add( + new KeyValuePair("Content-Length", payloadLength.ToString())); + + return response.Body.WriteAsync(json, 0, payloadLength); + } + + return _next(httpContext); + } +} + +public static class MultipleQueriesMiddlewareExtensions +{ + public static IApplicationBuilder UseMultipleQueries(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleUpdatesMiddleware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleUpdatesMiddleware.cs new file mode 100644 index 00000000000..5308afd3811 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleUpdatesMiddleware.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace appMpower; + +public class MultipleUpdatesMiddelware +{ + private readonly static KeyValuePair _headerServer = + new KeyValuePair("Server", new StringValues("k")); + private readonly static KeyValuePair _headerContentType = + new KeyValuePair("Content-Type", new StringValues("application/json")); + + private readonly RequestDelegate _next; + + public MultipleUpdatesMiddelware(RequestDelegate next) + { + _next = next; + } + + public unsafe Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/updates", StringComparison.Ordinal)) + { + var queryString = httpContext.Request.QueryString.ToString(); + int count; + Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out count); + count = count > 500 ? 500 : (count > 0 ? count : 1); + + var response = httpContext.Response; + response.Headers.Add(_headerServer); + response.Headers.Add(_headerContentType); + + int payloadLength; + IntPtr handlePointer; + + IntPtr bytePointer = NativeMethods.Updates(count, out payloadLength, out handlePointer); + byte[] json = new byte[payloadLength]; + Marshal.Copy(bytePointer, json, 0, payloadLength); + NativeMethods.FreeHandlePointer(handlePointer); + + response.Headers.Add( + new KeyValuePair("Content-Length", payloadLength.ToString())); + + return response.Body.WriteAsync(json, 0, payloadLength); + } + + return _next(httpContext); + } +} + +public static class MultipleUpdatesMiddelwareExtensions +{ + public static IApplicationBuilder UseMultipleUpdates(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/PlaintextMiddleware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/PlaintextMiddleware.cs new file mode 100644 index 00000000000..cf7ad20e54a --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/PlaintextMiddleware.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace appMpower; + +public unsafe class PlaintextMiddleware +{ + private readonly static KeyValuePair _headerServer = + new KeyValuePair("Server", new StringValues("k")); + private readonly static KeyValuePair _headerContentType = + new KeyValuePair("Content-Type", new StringValues("text/plain")); + private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!"); + + private readonly RequestDelegate _nextStage; + + public PlaintextMiddleware(RequestDelegate nextStage) + { + _nextStage = nextStage; + } + + public Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/plaintext", StringComparison.Ordinal)) + { + var payloadLength = _helloWorldPayload.Length; + var response = httpContext.Response; + response.Headers.Add(_headerServer); + response.Headers.Add(_headerContentType); + response.Headers.Add( + new KeyValuePair("Content-Length", payloadLength.ToString())); + + return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength); + } + + return _nextStage(httpContext); + } +} + +public static class PlaintextMiddlewareExtensions +{ + public static IApplicationBuilder UsePlainText(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/SingleQueryMiddleware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/SingleQueryMiddleware.cs new file mode 100644 index 00000000000..d43de484b66 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/SingleQueryMiddleware.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace appMpower; + +public class SingleQueryMiddleware +{ + private readonly static KeyValuePair _headerServer = + new KeyValuePair("Server", new StringValues("k")); + private readonly static KeyValuePair _headerContentType = + new KeyValuePair("Content-Type", new StringValues("application/json")); + + private readonly RequestDelegate _nextStage; + + public SingleQueryMiddleware(RequestDelegate nextStage) + { + _nextStage = nextStage; + } + + public unsafe Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/db", StringComparison.Ordinal)) + { + var response = httpContext.Response; + response.Headers.Add(_headerServer); + response.Headers.Add(_headerContentType); + + int payloadLength; + IntPtr handlePointer; + + IntPtr bytePointer = NativeMethods.Db(out payloadLength, out handlePointer); + byte[] json = new byte[payloadLength]; + Marshal.Copy(bytePointer, json, 0, payloadLength); + NativeMethods.FreeHandlePointer(handlePointer); + + response.Headers.Add( + new KeyValuePair("Content-Length", payloadLength.ToString())); + + return response.Body.WriteAsync(json, 0, payloadLength); + } + + return _nextStage(httpContext); + } +} + +public static class SingleQueryMiddlewareExtensions +{ + public static IApplicationBuilder UseSingleQuery(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/NativeMethods.cs b/frameworks/CSharp/appmpower/src/appMpower/NativeMethods.cs new file mode 100644 index 00000000000..e24104d47fe --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/NativeMethods.cs @@ -0,0 +1,62 @@ +using System; +using System.Runtime.InteropServices; + +public unsafe partial class NativeMethods +{ +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#endif + public static extern void Dbms(int dbms); + +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#endif + public static extern void DbProvider(int dbProvider); + +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl)] +#endif + public static extern void FreeHandlePointer(IntPtr handlePointer); + +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#endif + //public static extern byte* Db(out int length); + public static extern IntPtr Db(out int length, out IntPtr handlePointer); + +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#endif + public static extern IntPtr Fortunes(out int length, out IntPtr handlePointer); + +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#endif + public static extern IntPtr Query(int queries, out int length, out IntPtr handlePointer); + +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#endif + public static extern IntPtr Updates(int queries, out int length, out IntPtr handlePointer); + +#if DEBUG + [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#else + [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] +#endif + public static extern IntPtr DbById(int id, out int length, out IntPtr handlePointer); +} diff --git a/frameworks/CSharp/appmpower/src/appMpower/Program.cs b/frameworks/CSharp/appmpower/src/appMpower/Program.cs new file mode 100644 index 00000000000..f2f36aa627d --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Program.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace appMpower; + +class Program +{ + static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + static IHost BuildWebHost(string[] args) + { + var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .AddEnvironmentVariables(prefix: "ASPNETCORE_") + .AddCommandLine(args) + .Build(); + + var appSettings = config.GetSection("AppSettings").Get(); + + var host = Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseConfiguration(config) + .UseKestrel(options => + { + options.AddServerHeader = false; + options.AllowSynchronousIO = true; + }) + .UseStartup(); + }) + .Build(); + + return host; + } +} + +public class AppSettings +{ + public string Database { get; set; } +} diff --git a/frameworks/CSharp/appmpower/src/Kestrel/IJsonSerializer.cs b/frameworks/CSharp/appmpower/src/appMpower/Serializers/IJsonSerializer.cs similarity index 81% rename from frameworks/CSharp/appmpower/src/Kestrel/IJsonSerializer.cs rename to frameworks/CSharp/appmpower/src/appMpower/Serializers/IJsonSerializer.cs index 4f28a4cc538..eb59ff827ed 100644 --- a/frameworks/CSharp/appmpower/src/Kestrel/IJsonSerializer.cs +++ b/frameworks/CSharp/appmpower/src/appMpower/Serializers/IJsonSerializer.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace appMpower.Kestrel +namespace appMpower.Serializers { public interface IJsonSerializer { diff --git a/frameworks/CSharp/appmpower/src/JsonMessageSerializer.cs b/frameworks/CSharp/appmpower/src/appMpower/Serializers/JsonMessageSerializer.cs similarity index 58% rename from frameworks/CSharp/appmpower/src/JsonMessageSerializer.cs rename to frameworks/CSharp/appmpower/src/appMpower/Serializers/JsonMessageSerializer.cs index dca7f673368..c06636f1a31 100644 --- a/frameworks/CSharp/appmpower/src/JsonMessageSerializer.cs +++ b/frameworks/CSharp/appmpower/src/appMpower/Serializers/JsonMessageSerializer.cs @@ -1,13 +1,13 @@ using System.Text.Json; -namespace appMpower +namespace appMpower.Serializers { - public class JsonMessageSerializer : Kestrel.IJsonSerializer + public class JsonMessageSerializer : IJsonSerializer { public void Serialize(Utf8JsonWriter utf8JsonWriter, JsonMessage jsonMessage) { utf8JsonWriter.WriteStartObject(); - utf8JsonWriter.WriteString("message", jsonMessage.message); + utf8JsonWriter.WriteString("message", jsonMessage.Message); utf8JsonWriter.WriteEndObject(); } } diff --git a/frameworks/CSharp/appmpower/src/appMpower/Startup.cs b/frameworks/CSharp/appmpower/src/appMpower/Startup.cs new file mode 100644 index 00000000000..4b571cf8286 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Startup.cs @@ -0,0 +1,56 @@ +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace appMpower; + +public class Startup +{ + private readonly IConfiguration _configuration; + + public Startup(IConfiguration configuration) + { + _configuration = configuration; + } + + public void ConfigureServices(IServiceCollection services) + { + var appSettings = _configuration.Get(); + services.AddSingleton(appSettings); + +#if !DEBUG + #if ODBC + NativeMethods.DbProvider(1); //ODBC + #else + NativeMethods.DbProvider(0); //ADO + #endif + + #if POSTGRESQL + NativeMethods.Dbms(1); //PostgreSQL + #else + NativeMethods.Dbms(0); //MySQL + #endif +#endif + + var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana); + + settings.AllowCharacter('—'); + services.AddWebEncoders(options => + { + options.TextEncoderSettings = settings; + }); + } + + public void Configure(IApplicationBuilder app) + { + app.UsePlainText(); + app.UseJson(); + app.UseSingleQuery(); + app.UseCaching(); + app.UseFortunes(); + app.UseMultipleQueries(); + app.UseMultipleUpdates(); + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj b/frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj new file mode 100644 index 00000000000..5661d01ce61 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + Exe + true + + + + + + + + + + + + + + + + $(DefineConstants);ODBC + $(DefineConstants);POSTGRESQL + $(DefineConstants);MYSQL + + + \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/appsettings.json b/frameworks/CSharp/appmpower/src/appMpower/appsettings.json new file mode 100644 index 00000000000..0b1badf0626 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "None", + "Microsoft": "None", + "Microsoft.Hosting.Lifetime": "None" + } + } +} diff --git a/frameworks/CSharp/appmpower/src/nuget.config b/frameworks/CSharp/appmpower/src/appMpower/nuget.config similarity index 100% rename from frameworks/CSharp/appmpower/src/nuget.config rename to frameworks/CSharp/appmpower/src/appMpower/nuget.config diff --git a/frameworks/CSharp/appmpower/src/src.sln b/frameworks/CSharp/appmpower/src/src.sln new file mode 100644 index 00000000000..bedda48f506 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/src.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "appMpower.Orm", "appMpower.Orm\appMpower.Orm.csproj", "{1EC05247-7299-4F69-887B-3B746DF5F878}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "appMpower", "appMpower\appMpower.csproj", "{EB5D3496-53B9-49A7-A3AD-754078142F81}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1EC05247-7299-4F69-887B-3B746DF5F878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EC05247-7299-4F69-887B-3B746DF5F878}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EC05247-7299-4F69-887B-3B746DF5F878}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EC05247-7299-4F69-887B-3B746DF5F878}.Release|Any CPU.Build.0 = Release|Any CPU + {EB5D3496-53B9-49A7-A3AD-754078142F81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB5D3496-53B9-49A7-A3AD-754078142F81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB5D3496-53B9-49A7-A3AD-754078142F81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB5D3496-53B9-49A7-A3AD-754078142F81}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal 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/src/Minimal/Minimal.csproj b/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj index 783ec88ff32..01ed70de876 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..83105ed34be 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/Platform.csproj b/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj index 548e446ee0a..ef14e44b865 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj +++ b/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 true true preview @@ -19,9 +19,9 @@ - - - + + + 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/beetlex/PlatformBenchmarks/DBRaw.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/DBRaw.cs index ca668243fe7..6aae831fcbb 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/DBRaw.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/DBRaw.cs @@ -10,110 +10,44 @@ using Microsoft.Extensions.Caching.Memory; using Npgsql; +using System.Runtime.CompilerServices; namespace PlatformBenchmarks { - public class RawDb + public sealed class RawDb { - private readonly ConcurrentRandom _random; + private readonly MemoryCache _cache + = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(60) }); - private readonly DbProviderFactory _dbProviderFactory; - - private readonly static 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(); + private static DbProviderFactory _dbProviderFactory => Npgsql.NpgsqlFactory.Instance; + private readonly string _connectionString; - public static string _connectionString = null; - - public RawDb(ConcurrentRandom random, DbProviderFactory dbProviderFactory) + public RawDb(ConcurrentRandom random, string connectionString) { _random = random; - _dbProviderFactory = dbProviderFactory; - OnCreateCommand(); - } - private void OnCreateCommand() - { - SingleCommand = new Npgsql.NpgsqlCommand(); - SingleCommand.CommandText = "SELECT id, randomnumber FROM world WHERE id = @Id"; - mID = new Npgsql.NpgsqlParameter("@Id", _random.Next(1, 10001)); - SingleCommand.Parameters.Add(mID); - FortuneCommand = new Npgsql.NpgsqlCommand(); - FortuneCommand.CommandText = "SELECT id, message FROM fortune"; - } - - private DbCommand SingleCommand; - - private DbCommand FortuneCommand; - - private Npgsql.NpgsqlParameter mID; - - private static int ListDefaultSize = 8; - - private World[] mWorldBuffer = null; - - private World[] GetWorldBuffer() - { - if (mWorldBuffer == null) - mWorldBuffer = new World[512]; - return mWorldBuffer; - } - - private Fortune[] mFortunesBuffer = null; - - private Fortune[] GetFortuneBuffer() - { - if (mFortunesBuffer == null) - mFortunesBuffer = new Fortune[512]; - return mFortunesBuffer; + _connectionString = connectionString; } public async Task LoadSingleQueryRow() { - using (var db = _dbProviderFactory.CreateConnection()) - { - db.ConnectionString = _connectionString; - await db.OpenAsync(); - SingleCommand.Connection = db; - mID.TypedValue = _random.Next(1, 10001); - return await ReadSingleRow(db, SingleCommand); - - } - } - - async Task ReadSingleRow(DbConnection connection, DbCommand cmd) - { - using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow)) - + using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection()) { - await rdr.ReadAsync(); - return new World + connection.ConnectionString = _connectionString; + await connection.OpenAsync(); + var (cmd, _) = CreateReadCommand(connection); + using (var command = cmd) { - Id = rdr.GetInt32(0), - RandomNumber = rdr.GetInt32(1) - }; - } - } - public async Task> LoadMultipleQueriesRows(int count) - { - using (var db = _dbProviderFactory.CreateConnection()) - { - db.ConnectionString = _connectionString; - await db.OpenAsync(); - return await LoadMultipleRows(count, db); + return await ReadSingleRow(cmd); + } } } - - public Task LoadCachedQueries(int count) + public Task LoadCachedQueries(int count) { - var result = new World[count]; + var result = new CachedWorld[count]; var cacheKeys = _cacheKeys; var cache = _cache; var random = _random; @@ -121,42 +55,40 @@ public Task LoadCachedQueries(int count) { var id = random.Next(1, 10001); var key = cacheKeys[id]; - var data = cache.Get(key); - - if (data != null) + if (cache.TryGetValue(key, out var cached)) { - result[i] = data; + result[i] = (CachedWorld)cached; } else { - return LoadUncachedQueries(id, i, count, this, result); + return LoadUncachedQueries(_connectionString, id, i, count, this, result); } } return Task.FromResult(result); - static async Task LoadUncachedQueries(int id, int i, int count, RawDb rawdb, World[] result) + static async Task LoadUncachedQueries(string conn, int id, int i, int count, RawDb rawdb, CachedWorld[] result) { - using (var db = new NpgsqlConnection(_connectionString)) + using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection()) { - await db.OpenAsync(); - Func> create = async (entry) => - { - return await rawdb.ReadSingleRow(db, rawdb.SingleCommand); - }; + connection.ConnectionString = conn; + await connection.OpenAsync(); + var (cmd, idParameter) = rawdb.CreateReadCommand(connection); + using var command = cmd; + async Task create(ICacheEntry _) => await ReadSingleRow(cmd); + var cacheKeys = _cacheKeys; var key = cacheKeys[id]; - rawdb.SingleCommand.Connection = db; - rawdb.mID.TypedValue = id; + idParameter.TypedValue = id; + for (; i < result.Length; i++) { - var data = await _cache.GetOrCreateAsync(key, create); - result[i] = data; + result[i] = await rawdb._cache.GetOrCreateAsync(key, create); + id = rawdb._random.Next(1, 10001); - rawdb.SingleCommand.Connection = db; - rawdb.mID.TypedValue = id; + idParameter.TypedValue = id; key = cacheKeys[id]; } } @@ -164,215 +96,212 @@ static async Task LoadUncachedQueries(int id, int i, int count, RawDb r } } - - private async Task> LoadMultipleRows(int count, DbConnection db) + public async Task PopulateCache() { - SingleCommand.Connection = db; - SingleCommand.Parameters[0].Value = _random.Next(1, 10001); - var result = GetWorldBuffer(); - for (int i = 0; i < count; i++) + using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection()) { - result[i] = await ReadSingleRow(db, SingleCommand); - SingleCommand.Parameters[0].Value = _random.Next(1, 10001); + connection.ConnectionString = _connectionString; + await connection.OpenAsync(); + var (cmd, idParameter) = CreateReadCommand(connection); + using var command = cmd; + + var cacheKeys = _cacheKeys; + var cache = _cache; + for (var i = 1; i < 10001; i++) + { + idParameter.TypedValue = i; + cache.Set(cacheKeys[i], await ReadSingleRow(cmd)); + } } - return new ArraySegment(result, 0, count); + Console.WriteLine("Caching Populated"); } - public async Task> LoadFortunesRows() + public async Task LoadMultipleQueriesRows(int count) { - int count = 0; - var result = GetFortuneBuffer(); - using (var db = new NpgsqlConnection(_connectionString)) + var results = new World[count]; + + using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection()) { - await db.OpenAsync(); - FortuneCommand.Connection = db; - using (var rdr = await FortuneCommand.ExecuteReaderAsync()) + connection.ConnectionString = _connectionString; + await connection.OpenAsync(); + + using var batch = new NpgsqlBatch(connection) + { + // Inserts a PG Sync message between each statement in the batch, required for compliance with + // TechEmpower general test requirement 7 + // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview + EnableErrorBarriers = true + }; + + for (var i = 0; i < count; i++) { - while (await rdr.ReadAsync()) + batch.BatchCommands.Add(new() { - result[count] = (new Fortune - { - Id = rdr.GetInt32(0), - Message = rdr.GetString(1) - }); - count++; - } + CommandText = "SELECT id, randomnumber FROM world WHERE id = $1", + Parameters = { new NpgsqlParameter { TypedValue = _random.Next(1, 10001) } } + }); + } + + using var reader = await batch.ExecuteReaderAsync(); + + for (var i = 0; i < count; i++) + { + await reader.ReadAsync(); + results[i] = new World { Id = reader.GetInt32(0), RandomNumber = reader.GetInt32(1) }; + await reader.NextResultAsync(); } } - result[count] = (new Fortune { Message = "Additional fortune added at request time." }); - count++; - Array.Sort(result, 0, count); - return new ArraySegment(result, 0, count); + return results; } + public async Task LoadMultipleUpdatesRows(int count) { - using (var db = new NpgsqlConnection(_connectionString)) + var results = new World[count]; + + using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection()) { - await db.OpenAsync(); - var updateCmd = UpdateCommandsCached.PopCommand(count); - try + connection.ConnectionString = _connectionString; + await connection.OpenAsync(); + var (queryCmd, queryParameter) = CreateReadCommand(connection); + using (queryCmd) { - var command = updateCmd.Command; - command.Connection = db; - SingleCommand.Connection = db; - mID.TypedValue = _random.Next(1, int.MaxValue) % 10000 + 1; - var results = new World[count]; - for (int i = 0; i < count; i++) + for (var i = 0; i < results.Length; i++) { - results[i] = await ReadSingleRow(db, SingleCommand); - mID.TypedValue = _random.Next(1, int.MaxValue) % 10000 + 1; + results[i] = await ReadSingleRow(queryCmd); + queryParameter.TypedValue = _random.Next(1, 10001); } + } - for (int i = 0; i < count; i++) + using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), connection)) + { + for (var i = 0; i < results.Length; i++) { - var randomNumber = _random.Next(1, int.MaxValue) % 10000 + 1; - updateCmd.Parameters[i * 2].TypedValue = results[i].Id; - updateCmd.Parameters[i * 2 + 1].TypedValue = randomNumber; - //updateCmd.Parameters[i * 2].Value = results[i].Id; - //updateCmd.Parameters[i * 2 + 1].Value = randomNumber; + var randomNumber = _random.Next(1, 10001); + + updateCmd.Parameters.Add(new NpgsqlParameter { TypedValue = results[i].Id }); + updateCmd.Parameters.Add(new NpgsqlParameter { TypedValue = randomNumber }); + results[i].RandomNumber = randomNumber; } - await command.ExecuteNonQueryAsync(); - return results; - } - catch (Exception e_) - { - throw e_; - } - finally - { - UpdateCommandsCached.PushCommand(count, updateCmd); + await updateCmd.ExecuteNonQueryAsync(); } } - } - } + return results; + } - public sealed class CacheKey : IEquatable - { - private readonly int _value; + public async Task> LoadFortunesRows() + { + // Benchmark requirements explicitly prohibit pre-initializing the list size + var result = new List(); - public CacheKey(int value) - => _value = value; + using (var connection = (NpgsqlConnection)_dbProviderFactory.CreateConnection()) + { + connection.ConnectionString = _connectionString; + await connection.OpenAsync(); - public bool Equals(CacheKey key) - => key._value == _value; + using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", connection)) + { - public override bool Equals(object obj) - => ReferenceEquals(obj, this); + using (var rdr = await cmd.ExecuteReaderAsync()) + { - public override int GetHashCode() - => _value; + while (await rdr.ReadAsync()) + { + result.Add(new Fortune + { + Id = rdr.GetInt32(0), + Message = rdr.GetString(1) + }); + } + } + } + } + result.Add(new Fortune { Message = "Additional fortune added at request time." }); + result.Sort(); - public override string ToString() - => _value.ToString(); - } + return result; + } - internal class UpdateCommandsCached - { - private static System.Collections.Concurrent.ConcurrentStack[] mCacheTable - = new System.Collections.Concurrent.ConcurrentStack[1024]; - public static string[] IDParamereNames = new string[1024]; + private (NpgsqlCommand readCmd, NpgsqlParameter idParameter) CreateReadCommand(NpgsqlConnection connection) + { + var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = $1", connection); + var parameter = new NpgsqlParameter { TypedValue = _random.Next(1, 10001) }; - public static string[] RandomParamereNames = new string[1024]; + cmd.Parameters.Add(parameter); - static UpdateCommandsCached() - { - for (int i = 0; i < 1024; i++) - { - IDParamereNames[i] = $"@Id_{i}"; - RandomParamereNames[i] = $"@Random_{i}"; - mCacheTable[i] = new System.Collections.Concurrent.ConcurrentStack(); - } + return (cmd, parameter); } - private static CommandCacheItem CreatCommand(int count) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static async Task ReadSingleRow(NpgsqlCommand cmd) { - CommandCacheItem item = new CommandCacheItem(); - NpgsqlCommand cmd = new Npgsql.NpgsqlCommand(); - cmd.CommandText = BatchUpdateString.Query(count); - for (int i = 0; i < count; i++) - { - var id = new NpgsqlParameter(); - id.ParameterName = IDParamereNames[i]; - cmd.Parameters.Add(id); - item.Parameters.Add(id); - - var random = new NpgsqlParameter(); - random.ParameterName = RandomParamereNames[i]; - cmd.Parameters.Add(random); - item.Parameters.Add(random); - } - item.Command = cmd; - return item; + using var rdr = await cmd.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow); + await rdr.ReadAsync(); + return new World + { + Id = rdr.GetInt32(0), + RandomNumber = rdr.GetInt32(1) + }; } - public static void PushCommand(int count, CommandCacheItem cmd) - { - mCacheTable[count].Push(cmd); - } + private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray(); - public static CommandCacheItem PopCommand(int count) + public sealed class CacheKey : IEquatable { - if (mCacheTable[count].TryPop(out CommandCacheItem cmd)) - return cmd; - return CreatCommand(count); - } + private readonly int _value; - private static bool mInited = false; + public CacheKey(int value) + => _value = value; - public static void Init() - { - if (mInited) - return; - lock (typeof(UpdateCommandsCached)) - { - if (mInited) - return; - for (int i = 1; i <= 500; i++) - { - for (int k = 0; k < 10; k++) - { - var cmd = CreatCommand(i); - mCacheTable[i].Push(cmd); - } - } - mInited = true; - return; - } - } - } + public bool Equals(CacheKey key) + => key._value == _value; - class CommandCacheItem - { - public Npgsql.NpgsqlCommand Command { get; set; } + public override bool Equals(object obj) + => ReferenceEquals(obj, this); - public List> Parameters { get; private set; } = new List>(1024); + public override int GetHashCode() + => _value; + + public override string ToString() + => _value.ToString(); + } } - internal class BatchUpdateString + internal sealed class BatchUpdateString { private const int MaxBatch = 500; + internal static readonly string[] ParamNames = Enumerable.Range(0, MaxBatch * 2).Select(i => $"@p{i}").ToArray(); + private static string[] _queries = new string[MaxBatch + 1]; public static string Query(int batchSize) + => _queries[batchSize] is null + ? CreateBatch(batchSize) + : _queries[batchSize]; + + private static string CreateBatch(int batchSize) { - if (_queries[batchSize] != null) + var sb = StringBuilderCache.Acquire(); + + + sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES "); + var c = 1; + for (var i = 0; i < batchSize; i++) { - return _queries[batchSize]; + if (i > 0) + sb.Append(", "); + sb.Append($"(${c++}, ${c++})"); } + sb.Append(" ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id"); + - var lastIndex = batchSize - 1; - var sb = StringBuilderCache.Acquire(); - sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES "); - Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@Id_{i}, @Random_{i}), ")); - sb.Append($"(@Id_{lastIndex}, @Random_{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id"); return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb); } } diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/GMTDate.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/GMTDate.cs index 76e15ce9f7f..bfd6b79223a 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/GMTDate.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/GMTDate.cs @@ -49,7 +49,7 @@ public static GMTDate Default } } - public ArraySegment DATE + public Memory DATE { get; set; @@ -95,18 +95,20 @@ private void Init() }, null, 1000, 1000); } - private ArraySegment GetData() + private Memory GetData() { return GetData(DateTime.Now); } + + public void Write(IStreamWriter stream) { - var data = DATE; - stream.Write(data.Array, 0, data.Count); + + stream.Write(DATE.Span); } - private ArraySegment GetData(DateTime date) + private Memory GetData(DateTime date) { date = date.ToUniversalTime(); int offset13 = 0; @@ -190,7 +192,7 @@ private ArraySegment GetData(DateTime date) buffer[offset13] = _n; offset13++; - return new ArraySegment(GTM_BUFFER, 0, offset13); + return new Memory(GTM_BUFFER, 0, offset13); } } diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.Caching.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.Caching.cs index 2b2264ea60a..2794fcbdd82 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.Caching.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.Caching.cs @@ -33,7 +33,7 @@ public async Task caching(string queryString, IStreamWriter stream) ContentLengthMemory content = new ContentLengthMemory(); try { - var data = await _db.LoadCachedQueries(count); + var data = await DB.LoadCachedQueries(count); stream.Write(_jsonResultPreamble.Data, 0, _jsonResultPreamble.Length); content.Data = GetContentLengthMemory(stream); GMTDate.Default.Write(stream); diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.cs index e623afdd72b..117d19fee9a 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.cs @@ -47,7 +47,6 @@ public partial class HttpHandler : SesionBase private static readonly AsciiString _path_Fortunes = "/fortunes"; - private static readonly AsciiString _result_plaintext = "Hello, World!"; private static readonly AsciiString _cached_worlds = "/cached-worlds"; @@ -55,19 +54,6 @@ public partial class HttpHandler : SesionBase - private readonly static AsciiString _jsonPreamble = - _httpsuccess - + _headerContentTypeJson - + _headerServer - + _headerContentLength + _jsonPayloadSize.ToString() + _line; - - private readonly static AsciiString _plaintextPreamble = - _httpsuccess - + _headerContentTypeText - + _headerServer - + _headerContentLength + _result_plaintext.Length.ToString() + _line; - - private readonly static AsciiString _jsonResultPreamble = _httpsuccess + _headerContentTypeJson @@ -122,14 +108,14 @@ public HttpHandler() private Queue _Requests = new Queue(); - private RawDb _db; + public static RawDb DB; private RequestData _ReadRequest = null; public override void Connected(NetContext context) { base.Connected(context); this.Context = context; - _db = new RawDb(new ConcurrentRandom(), Npgsql.NpgsqlFactory.Instance); ; + } private int AnalysisUrl(ReadOnlySpan url) @@ -255,11 +241,11 @@ private void AnalysisAction(ReadOnlySequence line, out ActionType type, ou Span baseUrl = stackalloc byte[baseurlLen]; url.Slice(0, baseurlLen).CopyTo(baseUrl); - if (baseUrl.Length == _path_Plaintext.Length && baseUrl.StartsWith(_path_Plaintext)) + if (baseUrl.Length == _path_Plaintext.Length && baseUrl[1] == 'p') { type = ActionType.Plaintext; } - else if (baseUrl.Length == _path_Json.Length && baseUrl.StartsWith(_path_Json)) + else if (baseUrl.Length == _path_Json.Length && baseUrl[1] == 'j') { type = ActionType.Json; } diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.db.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.db.cs index 27c71d43f13..1604195625e 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.db.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.db.cs @@ -17,8 +17,8 @@ public async Task db(IStreamWriter stream) try { - var data = await _db.LoadSingleQueryRow(); - stream.Write(_jsonResultPreamble.Data, 0, _jsonResultPreamble.Length); + var data = await DB.LoadSingleQueryRow(); + stream.Write(_jsonResultPreamble.AsSpan()); content.Data = GetContentLengthMemory(stream); GMTDate.Default.Write(stream); stream.WriteSequenceNetStream.StartWriteLength(); diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.fortunes.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.fortunes.cs index 829c18bd68a..7bb2fc40acb 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.fortunes.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.fortunes.cs @@ -21,11 +21,11 @@ static HtmlEncoder CreateHtmlEncoder() return HtmlEncoder.Create(settings); } - private readonly static AsciiString _fortunesTableStart = "Fortunes"; - private readonly static AsciiString _fortunesRowStart = ""; - private readonly static AsciiString _fortunesTableEnd = "
idmessage
"; - private readonly static AsciiString _fortunesColumn = ""; - private readonly static AsciiString _fortunesRowEnd = "
"; + private static ReadOnlySpan _fortunesTableStart => "Fortunes"u8; + private static ReadOnlySpan _fortunesRowStart => ""u8; + private static ReadOnlySpan _fortunesTableEnd => "
idmessage
"u8; + private static ReadOnlySpan _fortunesColumn => ""u8; + private static ReadOnlySpan _fortunesRowEnd => "
"u8; public async Task fortunes(IStreamWriter stream) @@ -34,23 +34,23 @@ public async Task fortunes(IStreamWriter stream) try { - var data = await this._db.LoadFortunesRows(); + var data = await DB.LoadFortunesRows(); - stream.Write(_HtmlResultPreamble.Data, 0, _HtmlResultPreamble.Length); + stream.Write(_HtmlResultPreamble); content.Data = GetContentLengthMemory(stream); GMTDate.Default.Write(stream); stream.WriteSequenceNetStream.StartWriteLength(); - stream.Write(_fortunesTableStart.Data, 0, _fortunesTableStart.Length); + stream.Write(_fortunesTableStart); foreach (var item in data) { - stream.Write(_fortunesRowStart.Data, 0, _fortunesRowStart.Length); + stream.Write(_fortunesRowStart); stream.WriteString(item.Id.ToString(CultureInfo.InvariantCulture)); - stream.Write(_fortunesColumn.Data, 0, _fortunesColumn.Length); + stream.Write(_fortunesColumn); stream.WriteString(htmlEncoder.Encode(item.Message)); - stream.Write(_fortunesRowEnd.Data, 0, _fortunesRowEnd.Length); + stream.Write(_fortunesRowEnd); } - stream.Write(_fortunesTableEnd.Data, 0, _fortunesTableEnd.Length); + stream.Write(_fortunesTableEnd); } catch (Exception e_) { diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.json.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.json.cs index 1bfa2d8863f..4a379e98931 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.json.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.json.cs @@ -14,10 +14,14 @@ namespace PlatformBenchmarks public partial class HttpHandler { - + private static ReadOnlySpan _jsonPreamble => + "HTTP/1.1 200 OK\r\n"u8 + + "Server: B\r\n"u8 + + "Content-Type: application/json\r\n"u8 + + "Content-Length: 27\r\n"u8; public void Json(IStreamWriter stream) { - stream.Write(_jsonPreamble.Data, 0, _jsonPreamble.Length); + stream.Write(_jsonPreamble); GMTDate.Default.Write(stream); var jsonWriter = GetJsonWriter(stream); using (var unflush = stream.UnFlush()) @@ -27,7 +31,7 @@ public void Json(IStreamWriter stream) jsonWriter.WriteEndObject(); jsonWriter.Flush(); } - + } } } diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.plaintext.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.plaintext.cs index a011d52f7f4..fe50d21011d 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.plaintext.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.plaintext.cs @@ -2,6 +2,7 @@ using BeetleX.Light.Memory; using System; using System.Collections.Generic; +using System.Data; using System.Text; using System.Threading.Tasks; @@ -9,13 +10,27 @@ namespace PlatformBenchmarks { public partial class HttpHandler { - + private static ReadOnlySpan _plaintextPreamble => + "HTTP/1.1 200 OK\r\n"u8 + + "Server: B\r\n"u8 + + "Content-Type: text/plain\r\n"u8 + + "Content-Length: 13\r\n"u8; + + private static ReadOnlySpan _plainTextBody => "Hello, World!"u8; public void Plaintext(IStreamWriter stream) { - stream.Write(_plaintextPreamble.Data, 0, _plaintextPreamble.Length); - GMTDate.Default.Write(stream); - stream.Write(_result_plaintext.Data, 0, _result_plaintext.Length); + Span data = stream.WriteSequenceNetStream.GetWriteSpan(256); + var timedata = GMTDate.Default.DATE; + _plaintextPreamble.CopyTo(data); + data = data.Slice(_plaintextPreamble.Length); + timedata.Span.CopyTo(data); + data = data.Slice(timedata.Length); + _plainTextBody.CopyTo(data); + //stream.Write(_plaintextPreamble.AsSpan()); + //GMTDate.Default.Write(stream); + //stream.Write(_result_plaintext.Data.AsSpan()); + stream.WriteSequenceNetStream.WriteAdvance(_plaintextPreamble.Length + timedata.Length + _plainTextBody.Length); } } } diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.queries.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.queries.cs index 53eef8a31fb..4e7039176dd 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.queries.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.queries.cs @@ -31,7 +31,7 @@ public async ValueTask queries(string queryString, IStreamWriter stream) ContentLengthMemory content = new ContentLengthMemory(); try { - var data = await _db.LoadMultipleQueriesRows(count); + var data = await DB.LoadMultipleQueriesRows(count); stream.Write(_jsonResultPreamble.Data, 0, _jsonResultPreamble.Length); content.Data = GetContentLengthMemory(stream); GMTDate.Default.Write(stream); diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.updates.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.updates.cs index 255a430c070..99d938f32a5 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.updates.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpHandler.updates.cs @@ -32,7 +32,7 @@ public async ValueTask updates(string queryString, IStreamWriter stream) ContentLengthMemory content = new ContentLengthMemory(); try { - var data = await _db.LoadMultipleUpdatesRows(count); + var data = await DB.LoadMultipleUpdatesRows(count); stream.Write(_jsonResultPreamble.Data, 0, _jsonResultPreamble.Length); content.Data = GetContentLengthMemory(stream); diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpServer.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpServer.cs index b8ee7c5f495..b6cb8e3a1a0 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpServer.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/HttpServer.cs @@ -13,13 +13,19 @@ public class HttpServer : IHostedService { private static NetServer _apiServer; - public virtual Task StartAsync(CancellationToken cancellationToken) + public static string _connectionString; + + public virtual async Task StartAsync(CancellationToken cancellationToken) { + _connectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=16;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; + //_connectionString = "Server=localhost;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=16;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; ThreadPool.SetMinThreads(Environment.ProcessorCount * 2, Environment.ProcessorCount * 2); - Constants.MemorySegmentMinSize = 1024 * 8; - Constants.MemorySegmentMaxSize = 1024 * 8; + Constants.MemorySegmentMinSize = 1024 * 16; + Constants.MemorySegmentMaxSize = 1024 * 16; Constants.InitMemoryBlock(); - ArraySegment date = GMTDate.Default.DATE; + var date = GMTDate.Default.DATE; + HttpHandler.DB = new RawDb(new ConcurrentRandom(), HttpServer._connectionString); + await HttpHandler.DB.PopulateCache(); _apiServer = new NetServer(); _apiServer.Options.LogLevel = BeetleX.Light.Logs.LogLevel.Error; _apiServer.Options.AddLogOutputHandler(); @@ -30,20 +36,6 @@ public virtual Task StartAsync(CancellationToken cancellationToken) _apiServer.Start(); - if (!Program.UpDB) - { - RawDb._connectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=64;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; - //RawDb._connectionString = "Server=127.0.0.1;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=256;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3"; - } - else - { - - RawDb._connectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=64;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; - //RawDb._connectionString = "Server=127.0.0.1;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=64;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3"; - } - // ApiServer.Log(LogType.Info, null, $"Debug mode [{Program.Debug}]"); - return Task.CompletedTask; - } public virtual Task StopAsync(CancellationToken cancellationToken) diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/PlatformBenchmarks.csproj b/frameworks/CSharp/beetlex/PlatformBenchmarks/PlatformBenchmarks.csproj index 10202a882be..7518d6b0de0 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/PlatformBenchmarks.csproj +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/PlatformBenchmarks.csproj @@ -8,11 +8,11 @@
- - + + - +
diff --git a/frameworks/CSharp/beetlex/PlatformBenchmarks/Program.cs b/frameworks/CSharp/beetlex/PlatformBenchmarks/Program.cs index 3819a6c7e1d..ab262ef92a3 100644 --- a/frameworks/CSharp/beetlex/PlatformBenchmarks/Program.cs +++ b/frameworks/CSharp/beetlex/PlatformBenchmarks/Program.cs @@ -17,7 +17,6 @@ public static void Main(string[] args) //Debug = (args != null && args.Length > 0 && args[0] == "debug"); var data = GMTDate.Default.DATE; UpDB = (args != null && args.Length > 0 && args[0] == "updb"); - UpdateCommandsCached.Init(); //HttpServer server = new HttpServer(); //server.StartAsync(default); //Console.ReadLine(); 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/Dart/dart3/.dockerignore b/frameworks/Dart/dart3/.dockerignore new file mode 100644 index 00000000000..6a6cc8727a7 --- /dev/null +++ b/frameworks/Dart/dart3/.dockerignore @@ -0,0 +1,9 @@ +# From https://hub.docker.com/_/dart +.dockerignore +Dockerfile +build/ +.dart_tool/ +.git/ +.github/ +.gitignore +.packages diff --git a/frameworks/Dart/dart3/.gitignore b/frameworks/Dart/dart3/.gitignore new file mode 100644 index 00000000000..8ac40f21809 --- /dev/null +++ b/frameworks/Dart/dart3/.gitignore @@ -0,0 +1,5 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ +*.lock +!bin \ No newline at end of file diff --git a/frameworks/Dart/dart3/README.md b/frameworks/Dart/dart3/README.md new file mode 100644 index 00000000000..7152e705637 --- /dev/null +++ b/frameworks/Dart/dart3/README.md @@ -0,0 +1,22 @@ +# Dart 3 Benchmarking Test + +### Test Type Implementation Source Code + +- [JSON](server.dart) +- [PLAINTEXT](server.dart) + +## Important Libraries + +The tests were run with: + +- [Dart v3.4.4](https://dart.dev/) + +## Test URLs + +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext diff --git a/frameworks/Dart/dart3/analysis_options.yaml b/frameworks/Dart/dart3/analysis_options.yaml new file mode 100644 index 00000000000..572dd239d09 --- /dev/null +++ b/frameworks/Dart/dart3/analysis_options.yaml @@ -0,0 +1 @@ +include: package:lints/recommended.yaml diff --git a/frameworks/Dart/dart3/benchmark_config.json b/frameworks/Dart/dart3/benchmark_config.json new file mode 100644 index 00000000000..059fb368d0e --- /dev/null +++ b/frameworks/Dart/dart3/benchmark_config.json @@ -0,0 +1,26 @@ +{ + "framework": "dart3", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Stripped", + "classification": "Platform", + "database": "None", + "framework": "None", + "language": "Dart", + "flavor": "None", + "orm": "None", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "dart3", + "notes": "", + "versus": "None" + } + } + ] +} diff --git a/frameworks/Dart/dart3/bin/server.dart b/frameworks/Dart/dart3/bin/server.dart new file mode 100755 index 00000000000..f2b98dcd197 --- /dev/null +++ b/frameworks/Dart/dart3/bin/server.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:isolate'; + +final _encoder = JsonUtf8Encoder(); + +void main(List _) async { + /// Create an [Isolate] containinig an [HttpServer] + /// for each processor after the first + for (var i = 1; i < Platform.numberOfProcessors; i++) { + await Isolate.spawn(_startServer, _); + } + + /// Create a [HttpServer] for the first processor + await _startServer(_); +} + +/// Creates and setup a [HttpServer] +Future _startServer(List _) async { + /// Binds the [HttpServer] on `0.0.0.0:8080`. + final server = await HttpServer.bind( + InternetAddress('0.0.0.0', type: InternetAddressType.IPv4), + 8080, + shared: true, + ); + + /// Sets [HttpServer]'s [serverHeader]. + server + ..defaultResponseHeaders.clear() + ..serverHeader = 'dart'; + + /// Handles [HttpRequest]'s from [HttpServer]. + await for (final request in server) { + switch (request.uri.path) { + case '/json': + _jsonTest(request); + break; + case '/plaintext': + _plaintextTest(request); + break; + default: + _sendResponse(request, HttpStatus.notFound); + } + } +} + +/// Completes the given [request] by writing the [bytes] with the given +/// [statusCode] and [type]. +void _sendResponse( + HttpRequest request, + int statusCode, { + ContentType? type, + List bytes = const [], +}) => + request.response + ..statusCode = statusCode + ..headers.contentType = type + ..headers.date = DateTime.now() + ..contentLength = bytes.length + ..add(bytes) + ..close(); + +/// Completes the given [request] by writing the [response] as JSON. +void _sendJson(HttpRequest request, Object response) => _sendResponse( + request, + HttpStatus.ok, + type: ContentType.json, + bytes: _encoder.convert(response), + ); + +/// Completes the given [request] by writing the [response] as plain text. +void _sendText(HttpRequest request, String response) => _sendResponse( + request, + HttpStatus.ok, + type: ContentType.text, + bytes: utf8.encode(response), + ); + +/// Responds with the JSON test to the [request]. +void _jsonTest(HttpRequest request) => _sendJson( + request, + const {'message': 'Hello, World!'}, + ); + +/// Responds with the plaintext test to the [request]. +void _plaintextTest(HttpRequest request) => _sendText( + request, + 'Hello, World!', + ); diff --git a/frameworks/Dart/dart3/dart3.dockerfile b/frameworks/Dart/dart3/dart3.dockerfile new file mode 100644 index 00000000000..7e6e9053968 --- /dev/null +++ b/frameworks/Dart/dart3/dart3.dockerfile @@ -0,0 +1,14 @@ + +FROM dart:3.5 AS builder + +COPY . /app +WORKDIR /app +RUN mkdir build +RUN dart compile exe ./bin/server.dart -o build/server + +FROM scratch +COPY --from=builder /runtime/ / +COPY --from=builder /app/build /bin + +EXPOSE 8080 +CMD ["server"] \ No newline at end of file diff --git a/frameworks/Dart/dart3/pubspec.yaml b/frameworks/Dart/dart3/pubspec.yaml new file mode 100644 index 00000000000..2b351720c9b --- /dev/null +++ b/frameworks/Dart/dart3/pubspec.yaml @@ -0,0 +1,7 @@ +name: dartbenchmark +description: A benchmark of dart +environment: + sdk: '>=3.5.0 <4.0.0' + +dev_dependencies: + lints: ^4.0.0 diff --git a/frameworks/Elixir/phoenix/benchmark_config.json b/frameworks/Elixir/phoenix/benchmark_config.json index 9af25b9fbdc..2286bd85fbd 100644 --- a/frameworks/Elixir/phoenix/benchmark_config.json +++ b/frameworks/Elixir/phoenix/benchmark_config.json @@ -2,30 +2,6 @@ "framework": "phoenix", "tests": [{ "default": { - "json_url": "/json", - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "plaintext_url": "/plaintext", - "update_url": "/updates?queries=", - "cached_query_url": "/cached-queries?count=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "Phoenix", - "language": "Elixir", - "flavor": "None", - "orm": "full", - "platform": "beam", - "webserver": "cowboy", - "os": "Linux", - "database_os": "Linux", - "display_name": "Phoenix", - "notes": "", - "versus": "" - }, - "bandit": { "json_url": "/json", "db_url": "/db", "query_url": "/queries?queries=", @@ -45,9 +21,8 @@ "webserver": "bandit", "os": "Linux", "database_os": "Linux", - "display_name": "Phoenix-Bandit", - "notes": "", - "versus": "default" + "display_name": "Phoenix", + "notes": "" } }] } diff --git a/frameworks/Elixir/phoenix/config/bandit.exs b/frameworks/Elixir/phoenix/config/bandit.exs deleted file mode 100644 index ccaf648c115..00000000000 --- a/frameworks/Elixir/phoenix/config/bandit.exs +++ /dev/null @@ -1,43 +0,0 @@ -import Config - -config :hello, HelloWeb.Endpoint, - adapter: Bandit.PhoenixAdapter, - http: [port: 8080, ip: {0, 0, 0, 0}], - cache_static_lookup: false, - check_orgin: false, - debug_errors: false, - code_reloader: false, - server: true - - -config :hello, Hello.Repo, - username: "benchmarkdbuser", - password: "benchmarkdbpass", - database: "hello_world", - hostname: "tfb-database", - pool_size: 40, - queue_target: 5000, - log: false - -config :phoenix, :logger, false - -config :logger, - compile_time_purge_matching: [ - [level_lower_than: :error] - ], - level: :error, - backends: [] - -# ## SSL Support -# -# To get SSL working, you will need to add the `https` key -# to the previous section: -# -# config:hello, Hello.Endpoint, -# ... -# https: [port: 443, -# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), -# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] -# -# Where those two env variables point to a file on -# disk for the key and cert. diff --git a/frameworks/Elixir/phoenix/config/config.exs b/frameworks/Elixir/phoenix/config/config.exs index be5688f535b..e464e49d164 100755 --- a/frameworks/Elixir/phoenix/config/config.exs +++ b/frameworks/Elixir/phoenix/config/config.exs @@ -18,6 +18,14 @@ config :hello, HelloWeb.Endpoint, debug_errors: false, secret_key_base: "Z18ZjzZslFpKd8HB41IljqMavPiOKVF9y1DIQ+S2Ytg7Op0EIauwJgd7mtRStssx" +# Configure cache for world entities +config :hello, Hello.WorldCache, + gc_interval: :timer.hours(1), + max_size: 1_000_000, + allocated_memory: 100_000_000, + gc_cleanup_min_timeout: :timer.seconds(30), + gc_cleanup_max_timeout: :timer.minutes(30) + # Configures Elixir's Logger config :logger, :console, format: "$time $metadata[$level] $message\n", diff --git a/frameworks/Elixir/phoenix/config/prod.exs b/frameworks/Elixir/phoenix/config/prod.exs index 853a5ea247d..c920950ee04 100755 --- a/frameworks/Elixir/phoenix/config/prod.exs +++ b/frameworks/Elixir/phoenix/config/prod.exs @@ -1,9 +1,16 @@ import Config config :hello, HelloWeb.Endpoint, - url: [host: "0.0.0.0"], - http: [port: 8080, protocol_options: [max_keepalive: :infinity], backlog: 8096], - cache_static_lookup: false, + adapter: Bandit.PhoenixAdapter, + http: [ + port: 8080, + ip: {0, 0, 0, 0}, + http_options: [ + compress: false, + log_protocol_errors: false + ] + ], + compress: false, check_origin: false, debug_errors: false, code_reloader: false, @@ -14,7 +21,8 @@ config :hello, Hello.Repo, password: "benchmarkdbpass", database: "hello_world", hostname: "tfb-database", - pool_size: 40, + pool_count: 56, + pool_size: 15, queue_target: 5000, log: false @@ -26,17 +34,3 @@ config :logger, ], level: :error, backends: [] - -# ## SSL Support -# -# To get SSL working, you will need to add the `https` key -# to the previous section: -# -# config:hello, Hello.Endpoint, -# ... -# https: [port: 443, -# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), -# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] -# -# Where those two env variables point to a file on -# disk for the key and cert. diff --git a/frameworks/Elixir/phoenix/lib/hello/application.ex b/frameworks/Elixir/phoenix/lib/hello/application.ex index 58fed15fc63..49f1ef8ba03 100644 --- a/frameworks/Elixir/phoenix/lib/hello/application.ex +++ b/frameworks/Elixir/phoenix/lib/hello/application.ex @@ -6,7 +6,7 @@ defmodule Hello.Application do def start(_type, _args) do children = [ Hello.Repo, - {Hello.Cache, []}, + {Hello.WorldCache, []}, HelloWeb.Endpoint ] diff --git a/frameworks/Elixir/phoenix/lib/hello/cache.ex b/frameworks/Elixir/phoenix/lib/hello/cache.ex deleted file mode 100644 index 0f051a3fb72..00000000000 --- a/frameworks/Elixir/phoenix/lib/hello/cache.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule Hello.Cache do - use Nebulex.Cache, - otp_app: :hello, - adapter: Nebulex.Adapters.Local -end diff --git a/frameworks/Elixir/phoenix/lib/hello/world_cache.ex b/frameworks/Elixir/phoenix/lib/hello/world_cache.ex new file mode 100644 index 00000000000..e6cde7ab58e --- /dev/null +++ b/frameworks/Elixir/phoenix/lib/hello/world_cache.ex @@ -0,0 +1,31 @@ +defmodule Hello.WorldCache do + use Nebulex.Cache, + otp_app: :hello, + adapter: Nebulex.Adapters.Local + + alias Hello.Models.World + alias Hello.Repo + + def seed do + if not __MODULE__.has_key?(:seeded) do + World + |> Repo.all() + |> Enum.into([], &{&1.id, &1}) + |> __MODULE__.put_all() + + __MODULE__.put(:seeded, true) + end + end + + def fetch(id) do + case __MODULE__.get(id) do + nil -> + world = Repo.get(World, id) + :ok = __MODULE__.put(id, world) + world + + world -> + world + end + end +end diff --git a/frameworks/Elixir/phoenix/lib/hello_web.ex b/frameworks/Elixir/phoenix/lib/hello_web.ex index 78062f969be..1114ed22e51 100644 --- a/frameworks/Elixir/phoenix/lib/hello_web.ex +++ b/frameworks/Elixir/phoenix/lib/hello_web.ex @@ -62,9 +62,7 @@ defmodule HelloWeb do defp html_helpers do quote do # Use all HTML functionality (forms, tags, etc) - use Phoenix.HTML - # Core UI Components and translation - import HelloWeb.Gettext + import Phoenix.HTML # Routes generation with the ~p sigil unquote(verified_routes()) @@ -97,8 +95,8 @@ defmodule HelloWeb do end @doc """ - When used, dispatch to the appropriate controller/view/etc. - """ + When used, dispatch to the appropriate controller/view/etc. + """ defmacro __using__(which) when is_atom(which) do apply(__MODULE__, which, []) 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 a44c5edf099..59f2f5c2f35 100644 --- a/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_controller.ex +++ b/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_controller.ex @@ -1,13 +1,15 @@ defmodule HelloWeb.PageController do - alias Hello.Models.{Fortune, World} - use HelloWeb, :controller + alias Hello.Models.Fortune + alias Hello.Models.World alias Hello.Repo - alias Hello.Cache + alias Hello.WorldCache @random_max 10_000 + plug :accepts, ~w(html json) when action == :fortunes + def index(conn, _params) do json(conn, %{"TE Benchmarks\n" => "Started"}) end @@ -24,13 +26,12 @@ defmodule HelloWeb.PageController do end def queries(conn, params) do - :rand.seed(:exsp) - worlds = - Stream.repeatedly(&random_id/0) - |> Stream.uniq() - |> Stream.map(fn idx -> Repo.get(World, idx) end) - |> Enum.take(size(params["queries"])) + Repo.checkout(fn -> + params["queries"] + |> random_ids_sample() + |> Enum.map(&Repo.get(World, &1)) + end) json(conn, worlds) end @@ -41,71 +42,74 @@ defmodule HelloWeb.PageController do message: "Additional fortune added at request time." } - fortunes = [additional_fortune | Repo.all(Fortune)] + fortunes = + [additional_fortune | Repo.all(Fortune)] + |> Enum.sort(fn a, b -> a.message < b.message end) - render(conn, :fortunes, - fortunes: Enum.sort(fortunes, fn f1, f2 -> f1.message < f2.message end) - ) + render(conn, :fortunes, fortunes: fortunes) end def updates(conn, params) do - :rand.seed(:exsp) - - worlds = - Stream.repeatedly(&random_id/0) - |> Stream.uniq() - |> Stream.map(fn idx -> Repo.get(World, idx) end) - |> Stream.map(fn world -> %{id: world.id, randomnumber: :rand.uniform(@random_max)} end) - |> Enum.take(size(params["queries"])) + world_updates = + Repo.checkout(fn -> + params["queries"] + |> random_ids_sample() + |> Enum.sort() + # + # If this is not sorted it will intermittently generate: + # + # FAIL for http://tfb-server:8080/updates/20 + # Only 20470 executed queries in the database out of roughly 20480 expected. + # + |> Enum.map(fn id -> + world = Repo.get(World, id) + %{id: world.id, randomnumber: :rand.uniform(@random_max)} + end) + end) Repo.insert_all( World, - worlds, + world_updates, on_conflict: {:replace_all_except, [:id]}, conflict_target: [:id], returning: false ) - json(conn, worlds) + json(conn, world_updates) end - def plaintext(conn, _params) do - text(conn, "Hello, World!") + def plaintext(conn, _params) do + conn + |> put_resp_header("content-type", "text/plain") + |> send_resp(200, "Hello, World!") end def cached(conn, params) do - :rand.seed(:exsp) + WorldCache.seed() worlds = - Stream.repeatedly(&random_id/0) - |> Stream.uniq() - |> Stream.map(&get_cached_world/1) - |> Enum.take(size(params["count"])) + params["count"] + |> random_ids_sample() + |> Enum.map(&WorldCache.fetch(&1)) json(conn, worlds) end - defp get_cached_world(idx) do - case Cache.get(idx) do - nil -> - world = Repo.get(World, idx) - :ok = Cache.put(idx, world) - world - - world -> - world - end - end - defp random_id() do :rand.uniform(@random_max) end - defp size(nil), do: 1 - defp size(""), do: 1 + defp random_ids_sample(count) do + # Use the fastest rand algorithm + :rand.seed(:exsp) + + Stream.repeatedly(&random_id/0) + |> Stream.uniq() + |> Enum.take(size(count)) + end - defp size(queries) when is_bitstring(queries) do - case Integer.parse(queries) do + defp size(param_count) when is_bitstring(param_count) do + case Integer.parse(param_count) do {count, _} -> max(1, min(500, count)) _ -> 1 end diff --git a/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_html/fortunes.html.eex b/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_html/fortunes.html.heex similarity index 100% rename from frameworks/Elixir/phoenix/lib/hello_web/controllers/page_html/fortunes.html.eex rename to frameworks/Elixir/phoenix/lib/hello_web/controllers/page_html/fortunes.html.heex diff --git a/frameworks/Elixir/phoenix/lib/hello_web/endpoint.ex b/frameworks/Elixir/phoenix/lib/hello_web/endpoint.ex index f709d00a2e8..a764bbf653e 100644 --- a/frameworks/Elixir/phoenix/lib/hello_web/endpoint.ex +++ b/frameworks/Elixir/phoenix/lib/hello_web/endpoint.ex @@ -20,4 +20,3 @@ defmodule HelloWeb.Endpoint do plug HelloWeb.HeadersPlug plug HelloWeb.Router end - diff --git a/frameworks/Elixir/phoenix/lib/hello_web/router.ex b/frameworks/Elixir/phoenix/lib/hello_web/router.ex index 1634f3403fd..7e3caed3b8f 100755 --- a/frameworks/Elixir/phoenix/lib/hello_web/router.ex +++ b/frameworks/Elixir/phoenix/lib/hello_web/router.ex @@ -1,13 +1,7 @@ defmodule HelloWeb.Router do use HelloWeb, :router - pipeline :browser do - plug :accepts, ~w(html json) - end - scope "/", HelloWeb do - pipe_through [:browser] - get "/json", PageController, :_json get "/db", PageController, :db get "/queries", PageController, :queries diff --git a/frameworks/Elixir/phoenix/mix.exs b/frameworks/Elixir/phoenix/mix.exs index fa48c24784d..c11d586e566 100755 --- a/frameworks/Elixir/phoenix/mix.exs +++ b/frameworks/Elixir/phoenix/mix.exs @@ -4,8 +4,8 @@ defmodule Hello.Mixfile do def project do [ app: :hello, - version: "0.1.0", - elixir: "~> 1.14", + version: "1.1.1", + elixir: "~> 1.17", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, deps: deps() @@ -29,17 +29,17 @@ defmodule Hello.Mixfile do # Type `mix help deps` for examples and options defp deps do [ - {:bandit, "~> 1.0.0-pre.5"}, + {:bandit, "1.5.7"}, {:gettext, "~> 0.20"}, - {:ecto_sql, "~> 3.7"}, + {:ecto_sql, "~> 3.10"}, {:jason, "~> 1.2"}, {:phoenix, "~> 1.7"}, - {:phoenix_ecto, "~> 4.4"}, - {:phoenix_html, "~> 3.2"}, {:phoenix_live_view, "~> 0.18"}, + {:phoenix_ecto, "~> 4.4"}, + {:phoenix_html, "~> 4.1"}, {:plug_cowboy, "~> 2.5"}, - {:postgrex, "~> 0.15"}, - {:nebulex, "~> 2.4"} + {:postgrex, ">= 0.0.0"}, + {:nebulex, "~> 2.6"} ] end end diff --git a/frameworks/Elixir/phoenix/mix.lock b/frameworks/Elixir/phoenix/mix.lock index 415edcb0107..0c47cab2458 100644 --- a/frameworks/Elixir/phoenix/mix.lock +++ b/frameworks/Elixir/phoenix/mix.lock @@ -1,32 +1,32 @@ %{ - "bandit": {:hex, :bandit, "1.0.0-pre.5", "94b668f987a3b63f53123d2bdef1aaab73439eb02fcef7eb99032038206cab54", [:mix], [{:hpax, "~> 0.1.1", [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-pre", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "686da83f0c2cabd68cc221067ab43bc50af88c3e7fc8ef824a0b7039a978e383"}, - "castore": {:hex, :castore, "1.0.2", "0c6292ecf3e3f20b7c88408f00096337c4bfd99bd46cc2fe63413ddbe45b3573", [:mix], [], "hexpm", "40b2dd2836199203df8500e4a270f10fc006cc95adc8a319e148dc3077391d96"}, - "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, + "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.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, - "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, + "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, + "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.10.1", "c6757101880e90acc6125b095853176a02da8f1afe056f91f1f90b80c9389822", [:mix], [{:decimal, "~> 1.6 or ~> 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", "d2ac4255f1601bdf7ac74c0ed971102c6829dc158719b94bd30041bbad77f87a"}, - "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 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", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, - "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, - "gettext": {:hex, :gettext, "0.22.2", "6bfca374de34ecc913a28ba391ca184d88d77810a3e427afa8454a71a51341ac", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "8a2d389673aea82d7eae387e6a2ccc12660610080ae7beb19452cfdc1ec30f60"}, - "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, - "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, - "nebulex": {:hex, :nebulex, "2.5.1", "8ffbde30643e76d6cec712281ca68ab05f73170de9e758a39bc7e4e6987f608f", [: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", "8d0d3800d98c68ee19b229b7fe35fac0192ab5963a573612cf74a388e083bccf"}, - "phoenix": {:hex, :phoenix, "1.7.3", "4d8eca2c020c9ed81a28e7a8c60e0a4f6f9f7f6e12eb91dfd01301eac07424c1", [: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.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "6b1bc308758f95ecf3e0d795389440a2ca88a903e0fda1f921c780918c16d640"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.2", "b21bd01fdeffcfe2fab49e4942aa938b6d3e89e93a480d4aee58085560a0bc0d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "70242edd4601d50b69273b057ecf7b684644c19ee750989fd555625ae4ce8f5d"}, - "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "0.19.0", "de5643d03e3cdf5ff19cd45b5d14543a3b1ad8551d529f6b24246e88a6c6f1b8", [:mix], [{: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", [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]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a650b6f814c4f386314b98f1aebf92f8652649166612f84ef2e60a20894addfa"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.2", "a4950b63ace57720b0fc1c6da083c53346b36f99021de89595cc4f026288ff51", [:mix], [], "hexpm", "45741676a94c71f9afdfed9d22d49b6856c026ff504db04e3dc03a1d86f8201c"}, - "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, - "plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [: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", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, - "postgrex": {:hex, :postgrex, "0.17.1", "01c29fd1205940ee55f7addb8f1dc25618ca63a8817e56fac4f6846fc2cddcbe", [: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", "14b057b488e73be2beee508fb1955d8db90d6485c6466428fe9ccf1d6692a555"}, + "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"}, + "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"}, + "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"}, + "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"}, - "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "thousand_island": {:hex, :thousand_island, "1.0.0-pre.3", "67b31809769736031b240339fa5096a6e491b7d7ec5e0e37ee80cab10e8712a9", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "75602046418b2510aac3c968b2941778187fe88256010116834903b806d77f53"}, - "websock": {:hex, :websock, "0.5.1", "c496036ce95bc26d08ba086b2a827b212c67e7cabaa1c06473cd26b40ed8cf10", [:mix], [], "hexpm", "b9f785108b81cd457b06e5f5dabe5f65453d86a99118b2c0a515e1e296dc2d2c"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.1", "292e6c56724e3457e808e525af0e9bcfa088cc7b9c798218e78658c7f9b85066", [: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", "8e2e1544bfde5f9d0442f9cec2f5235398b224f75c9e06b60557debf64248ec1"}, + "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"}, + "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"}, } diff --git a/frameworks/Elixir/phoenix/phoenix-bandit.dockerfile b/frameworks/Elixir/phoenix/phoenix-bandit.dockerfile deleted file mode 100644 index 103350fc6db..00000000000 --- a/frameworks/Elixir/phoenix/phoenix-bandit.dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -ARG ELIXIR="1.14.5" -ARG ERLANG="26.0" -ARG ALPINE="3.17.3" - -ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR}-erlang-${ERLANG}-alpine-${ALPINE}" -ARG RUNNER_IMAGE="alpine:${ALPINE}" - -FROM ${BUILDER_IMAGE} AS builder - -ARG MIX_ENV="bandit" - -RUN mix local.hex --force && \ - mix local.rebar --force - -COPY mix.exs mix.lock ./ -RUN mix deps.get --force --only prod - -COPY config ./config - -RUN mix deps.compile - -COPY priv ./priv -COPY lib ./lib - -COPY rel ./rel -RUN mix release --force --path /export - -# start a new build stage so that the final image will only contain -# the compiled release and other runtime necessities -FROM ${RUNNER_IMAGE} - -RUN apk add --no-cache libstdc++ openssl ncurses-libs - -COPY --from=builder /export /opt - -EXPOSE 8080 - -ENTRYPOINT ["/opt/bin/hello"] -CMD ["start"] diff --git a/frameworks/Elixir/phoenix/phoenix.dockerfile b/frameworks/Elixir/phoenix/phoenix.dockerfile index c9e0d7701b2..dcf8df3dd20 100644 --- a/frameworks/Elixir/phoenix/phoenix.dockerfile +++ b/frameworks/Elixir/phoenix/phoenix.dockerfile @@ -1,6 +1,6 @@ -ARG ELIXIR="1.14.5" -ARG ERLANG="26.0" -ARG ALPINE="3.17.3" +ARG ELIXIR="1.17.2" +ARG ERLANG="27.0.1" +ARG ALPINE="3.19.3" ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR}-erlang-${ERLANG}-alpine-${ALPINE}" ARG RUNNER_IMAGE="alpine:${ALPINE}" diff --git a/frameworks/Elixir/plug/elixir-plug-ecto.dockerfile b/frameworks/Elixir/plug/elixir-plug-ecto.dockerfile index a674dd80178..c943ba48991 100644 --- a/frameworks/Elixir/plug/elixir-plug-ecto.dockerfile +++ b/frameworks/Elixir/plug/elixir-plug-ecto.dockerfile @@ -1,6 +1,6 @@ -ARG ELIXIR="1.14.2" -ARG ERLANG="25.1.2" -ARG ALPINE="3.16.2" +ARG ELIXIR="1.17.2" +ARG ERLANG="27.0.1" +ARG ALPINE="3.19.3" ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR}-erlang-${ERLANG}-alpine-${ALPINE}" ARG RUNNER_IMAGE="alpine:${ALPINE}" diff --git a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/cached-world.ex b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/cached-world.ex index c80b71053e7..60b441557c5 100644 --- a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/cached-world.ex +++ b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/cached-world.ex @@ -11,10 +11,10 @@ defmodule FrameworkBenchmarks.Handlers.CachedWorld do :rand.uniform(10_000) end) - {:ok, json} = + json = ids |> Enum.map(&FrameworkBenchmarks.CachedWorld.get/1) - |> Jason.encode() + |> Jason.encode_to_iodata!() conn |> Plug.Conn.put_resp_content_type("application/json") diff --git a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/db.ex b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/db.ex index b1abba5dd8b..101b7e5f8fd 100644 --- a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/db.ex +++ b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/db.ex @@ -5,11 +5,9 @@ defmodule FrameworkBenchmarks.Handlers.DB do def handle(conn) do id = :rand.uniform(10_000) - {:ok, json} = + json = FrameworkBenchmarks.Repo.get(FrameworkBenchmarks.Models.World, id) - |> Map.from_struct() - |> Map.drop([:__meta__]) - |> Jason.encode() + |> Jason.encode_to_iodata!() conn |> Plug.Conn.put_resp_content_type("application/json") diff --git a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/json.ex b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/json.ex index c013110a6de..3a7ada06cdb 100644 --- a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/json.ex +++ b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/json.ex @@ -3,7 +3,7 @@ defmodule FrameworkBenchmarks.Handlers.JSON do This is the handle for the /json route """ def handle(conn) do - {:ok, json} = Jason.encode(%{message: "Hello, World!"}) + json = Jason.encode_to_iodata!(%{message: "Hello, World!"}) conn |> Plug.Conn.put_resp_content_type("application/json") diff --git a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/query.ex b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/query.ex index b4e83026a82..6ad2af9061a 100644 --- a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/query.ex +++ b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/query.ex @@ -5,7 +5,7 @@ defmodule FrameworkBenchmarks.Handlers.Query do def handle(conn) do number_of_queries = FrameworkBenchmarks.Handlers.Helpers.parse_queries(conn, "queries") - records = + json = 1..number_of_queries |> Enum.map(fn _ -> :rand.uniform(10_000) @@ -15,16 +15,8 @@ defmodule FrameworkBenchmarks.Handlers.Query do FrameworkBenchmarks.Repo.get(FrameworkBenchmarks.Models.World, &1) end) ) - |> Enum.map(&Task.await(&1)) - - {:ok, json} = - records - |> Enum.map(fn record -> - record - |> Map.from_struct() - |> Map.drop([:__meta__]) - end) - |> Jason.encode() + |> Enum.map(&Task.await(&1, :infinity)) + |> Jason.encode_to_iodata!() conn |> Plug.Conn.put_resp_content_type("application/json") diff --git a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/update.ex b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/update.ex index d06744adfde..0b5ad54c4b8 100644 --- a/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/update.ex +++ b/frameworks/Elixir/plug/lib/framework_benchmarks/handlers/update.ex @@ -22,7 +22,7 @@ defmodule FrameworkBenchmarks.Handlers.Update do :rand.uniform(10_000) end) - records = + json = ids |> Enum.map( &Task.async(fn -> @@ -38,16 +38,8 @@ defmodule FrameworkBenchmarks.Handlers.Update do |> FrameworkBenchmarks.Repo.update!() end) ) - |> Enum.map(&Task.await(&1)) - - {:ok, json} = - records - |> Enum.map(fn record -> - record - |> Map.from_struct() - |> Map.drop([:__meta__]) - end) - |> Jason.encode() + |> Enum.map(&Task.await(&1, :infinity)) + |> Jason.encode_to_iodata!() conn |> Plug.Conn.put_resp_content_type("application/json") diff --git a/frameworks/Elixir/plug/lib/framework_benchmarks/models/world.ex b/frameworks/Elixir/plug/lib/framework_benchmarks/models/world.ex index 549d8415a9e..d3db33611c1 100644 --- a/frameworks/Elixir/plug/lib/framework_benchmarks/models/world.ex +++ b/frameworks/Elixir/plug/lib/framework_benchmarks/models/world.ex @@ -1,6 +1,7 @@ defmodule FrameworkBenchmarks.Models.World do use Ecto.Schema + @derive {Jason.Encoder, only: [:id, :randomnumber]} schema "world" do field(:randomnumber, :integer) end diff --git a/frameworks/Elixir/plug/mix.exs b/frameworks/Elixir/plug/mix.exs index 1660a684b65..dcb29917a46 100644 --- a/frameworks/Elixir/plug/mix.exs +++ b/frameworks/Elixir/plug/mix.exs @@ -4,8 +4,8 @@ defmodule FrameworkBenchmarks.MixProject do def project do [ app: :framework_benchmarks, - version: "1.1.0", - elixir: "~> 1.14", + version: "1.1.1", + elixir: "~> 1.17", start_permanent: Mix.env() == :prod, deps: deps() ] @@ -24,10 +24,10 @@ defmodule FrameworkBenchmarks.MixProject do [ {:plug_cowboy, "~> 2.5"}, {:jason, "~> 1.4"}, - {:ecto_sql, "~> 3.8"}, - {:postgrex, "~> 0.16"}, - {:cachex, "~> 3.4"}, - {:phoenix_html, "~> 3.2"} + {:ecto_sql, "~> 3.10"}, + {:postgrex, ">= 0.0.0"}, + {:cachex, "~> 3.6"}, + {:phoenix_html, "~> 4.1"} ] end end diff --git a/frameworks/Elixir/plug/mix.lock b/frameworks/Elixir/plug/mix.lock index 324fedc25f8..fcc35f892b8 100644 --- a/frameworks/Elixir/plug/mix.lock +++ b/frameworks/Elixir/plug/mix.lock @@ -1,27 +1,23 @@ %{ "cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"}, - "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, - "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, + "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.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, - "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, + "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, + "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.10.1", "c6757101880e90acc6125b095853176a02da8f1afe056f91f1f90b80c9389822", [:mix], [{:decimal, "~> 1.6 or ~> 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", "d2ac4255f1601bdf7ac74c0ed971102c6829dc158719b94bd30041bbad77f87a"}, - "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 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", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, - "eljiffy": {:hex, :eljiffy, "1.3.0", "7e584be454c5ec3fc3ae472eedb4cb2185e9ed6cd863df383ef601de3f3b27fd", [:mix], [{:jiffy, "~> 1.0", [hex: :jiffy, repo: "hexpm", optional: false]}], "hexpm", "90420512d60fb45bc9c09221b4d89cc539c9bfefc1c62f24cb3e2cb13acf2215"}, + "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [: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", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, + "ecto_sql": {:hex, :ecto_sql, "3.11.3", "4eb7348ff8101fbc4e6bbc5a4404a24fecbe73a3372d16569526b0cf34ebc195", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 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", "e5f36e3d736b99c7fee3e631333b8394ade4bafe9d96d35669fca2d81c2be928"}, "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, - "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, - "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, - "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, - "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, - "plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [: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", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, - "postgrex": {:hex, :postgrex, "0.17.1", "01c29fd1205940ee55f7addb8f1dc25618ca63a8817e56fac4f6846fc2cddcbe", [: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", "14b057b488e73be2beee508fb1955d8db90d6485c6466428fe9ccf1d6692a555"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"}, + "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"}, + "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"}, - "sleeplocks": {:hex, :sleeplocks, "1.1.2", "d45aa1c5513da48c888715e3381211c859af34bee9b8290490e10c90bb6ff0ca", [:rebar3], [], "hexpm", "9fe5d048c5b781d6305c1a3a0f40bb3dfc06f49bf40571f3d2d0c57eaa7f59a5"}, + "sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "ucol": {:hex, :ucol, "2.0.0", "64f9589d682dac6ca59252e1222e22697784f74addd0b88c5e34d53d267356bb", [:rebar3], [], "hexpm", "b544b88ce034d1d1ab58e093744cbded9a1e8b05006870b4d3865d6cd5066a21"}, - "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, + "unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"}, } diff --git a/frameworks/FSharp/oxpecker/README.md b/frameworks/FSharp/oxpecker/README.md index ecd2284661d..a35cbf08522 100644 --- a/frameworks/FSharp/oxpecker/README.md +++ b/frameworks/FSharp/oxpecker/README.md @@ -1,5 +1,5 @@ # Oxpecker Tests on Linux -This includes tests for plaintext, json, and fortunes HTML serialization. +This includes tests for plaintext, json, fortunes, single query, mutliple queries and data updates. ## Infrastructure Software Versions @@ -18,9 +18,6 @@ This includes tests for plaintext, json, and fortunes HTML serialization. **Web Stack** * [Oxpecker](https://github.com/Lanayx/Oxpecker) -* [Dapper](https://github.com/DapperLib/Dapper) -* ASP.NET Core - -## Paths & Source for Tests - -All source code is inside `Program.fs`. +* [Npgsql](https://github.com/npgsql/npgsql) +* [SpanJson](https://github.com/Tornhoof/SpanJson) +* ASP.NET Core \ No newline at end of file diff --git a/frameworks/FSharp/oxpecker/benchmark_config.json b/frameworks/FSharp/oxpecker/benchmark_config.json index c0df28d86a3..562c9514420 100644 --- a/frameworks/FSharp/oxpecker/benchmark_config.json +++ b/frameworks/FSharp/oxpecker/benchmark_config.json @@ -15,7 +15,7 @@ "database": "Postgres", "framework": "Oxpecker", "language": "F#", - "orm": "Micro", + "orm": "Raw", "platform": ".NET", "flavor": "CoreCLR", "webserver": "Kestrel", @@ -23,7 +23,7 @@ "database_os": "Linux", "display_name": "Oxpecker", "notes": "", - "versus": "aspcore" + "versus": "aspnetcore" } } ] diff --git a/frameworks/FSharp/oxpecker/config.toml b/frameworks/FSharp/oxpecker/config.toml index dd5e34c8b96..75e7f904754 100644 --- a/frameworks/FSharp/oxpecker/config.toml +++ b/frameworks/FSharp/oxpecker/config.toml @@ -13,7 +13,7 @@ classification = "fullstack" database = "Postgres" database_os = "Linux" os = "Linux" -orm = "micro" +orm = "Raw" platform = ".NET" webserver = "Kestrel" -versus = "aspcore" \ No newline at end of file +versus = "aspnetcore" \ No newline at end of file diff --git a/frameworks/FSharp/oxpecker/oxpecker.dockerfile b/frameworks/FSharp/oxpecker/oxpecker.dockerfile index 35a8c509c96..4f6676892c3 100644 --- a/frameworks/FSharp/oxpecker/oxpecker.dockerfile +++ b/frameworks/FSharp/oxpecker/oxpecker.dockerfile @@ -6,6 +6,7 @@ RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime ENV DOTNET_ReadyToRun 0 +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 a9e0ad2c0ba..e9aa702a5a1 100644 --- a/frameworks/FSharp/oxpecker/src/App/App.fsproj +++ b/frameworks/FSharp/oxpecker/src/App/App.fsproj @@ -6,13 +6,17 @@ + + + - - - + + + + \ No newline at end of file diff --git a/frameworks/FSharp/oxpecker/src/App/Common.fs b/frameworks/FSharp/oxpecker/src/App/Common.fs new file mode 100644 index 00000000000..47a63e2108e --- /dev/null +++ b/frameworks/FSharp/oxpecker/src/App/Common.fs @@ -0,0 +1,34 @@ +namespace App + +open System +open System.Collections.Generic + +[] +module Common = + + [] + [] + type JsonMessage = { + message : string + } + + [] + type Fortune = { + id: int + message: string + } + + [] + [] + type World = { + id: int + randomnumber: int + } + + [] + let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3" + + let FortuneComparer = { + new IComparer with + member self.Compare(a,b) = String.CompareOrdinal(a.message, b.message) + } diff --git a/frameworks/FSharp/oxpecker/src/App/Db.fs b/frameworks/FSharp/oxpecker/src/App/Db.fs new file mode 100644 index 00000000000..2c471bada1d --- /dev/null +++ b/frameworks/FSharp/oxpecker/src/App/Db.fs @@ -0,0 +1,113 @@ +namespace App + +open System +open System.Data +open System.Data.Common +open System.Text +open Npgsql + + +[] +module Db = + let loadFortunes () = + let result = ResizeArray() + task { + use db = new NpgsqlConnection(ConnectionString) + use cmd = db.CreateCommand(CommandText = "SELECT id, message FROM fortune") + do! db.OpenAsync() + use! rdr = cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection) + while! rdr.ReadAsync() do + result.Add { id = rdr.GetInt32(0); message = rdr.GetString(1) } + return result + } + + let private createReadCommand (connection: NpgsqlConnection) = + let cmd = connection.CreateCommand( + CommandText = "SELECT id, randomnumber FROM world WHERE id = @Id" + ) + let id = NpgsqlParameter( + ParameterName = "@Id", + DbType = DbType.Int32, + TypedValue = Random.Shared.Next(1, 10001) + ) + cmd.Parameters.Add(id) |> ignore + cmd + + let private readSingleRow (cmd: NpgsqlCommand) = + task { + use! rdr = cmd.ExecuteReaderAsync(CommandBehavior.SingleRow) + let! _ = rdr.ReadAsync() + return { id = rdr.GetInt32(0); randomnumber = rdr.GetInt32(1) } + } + + let loadSingleRow () = + task { + use db = new NpgsqlConnection(ConnectionString) + 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 + for i in 0..result.Length-1 do + cmd.Parameters["@Id"].Value <- Random.Shared.Next(1, 10001) + let! row = readSingleRow cmd + result[i] <- row + return result + } + + let loadMultipleRows (count: int) = + task { + use db = new NpgsqlConnection(ConnectionString) + do! db.OpenAsync() + return! readMultipleRows count db + } + + let private maxBatch = 500 + let private queries = Array.zeroCreate (maxBatch + 1) + let private batchUpdateString batchSize = + match queries[batchSize] with + | null -> + let lastIndex = batchSize - 1 + let sb = StringBuilder() + 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() + queries[batchSize] <- result + result + | q -> + q + + // fill cache + let _ = [| 0..maxBatch |] |> Array.map batchUpdateString + + let private paramNames = + seq { 0..maxBatch*2 } + |> Seq.map (fun i -> struct($"@Rn_{i}", $"@Id_{i}")) + |> Seq.toArray + + let private generateParameters (results: World[]) (command: NpgsqlCommand) = + 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 + results[i] <- { results[i] with randomnumber = randomNumber } + + let doMultipleUpdates (count: int) = + task { + use conn = new NpgsqlConnection(ConnectionString) + do! conn.OpenAsync() + let! results = readMultipleRows count conn + use cmd = conn.CreateCommand(CommandText = batchUpdateString count) + do generateParameters results cmd + let! _ = cmd.ExecuteNonQueryAsync() + return results + } diff --git a/frameworks/FSharp/oxpecker/src/App/Program.fs b/frameworks/FSharp/oxpecker/src/App/Program.fs index bdb95afa593..895c54c9693 100644 --- a/frameworks/FSharp/oxpecker/src/App/Program.fs +++ b/frameworks/FSharp/oxpecker/src/App/Program.fs @@ -1,70 +1,46 @@ namespace App open System -open System.Collections.Generic open Oxpecker - -[] -module Common = - - [] - [] - type JsonMessage = { - message : string - } - - [] - type Fortune = { - id: int - message: string - } - - [] - let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000" - - let FortuneComparer = { - new IComparer with - member self.Compare(a,b) = String.CompareOrdinal(a.message, b.message) - } +open System.Runtime.InteropServices [] module HtmlViews = open Oxpecker.ViewEngine - let private fortunesHead = - head() { - title() { raw "Fortunes" } - } - - let private layout (content: HtmlElement) = - html() { - fortunesHead - body() { content } - } - - let private fortunesTableHeader = - tr() { - th() { raw "id" } - th() { raw "message" } - } - - let fortunes fortunesData = - table() { - fortunesTableHeader - for fortune in fortunesData do - tr() { - td() { raw <| string fortune.id } - td() { fortune.message } + let private head, tail = + (fun (content: HtmlElement) -> + html() { + head() { + title() { "Fortunes" } + } + body() { + table() { + tr() { + th() { "id" } + th() { "message" } + } + content + } } - } |> layout + } :> 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 Dapper - open Npgsql open System.Text open Microsoft.AspNetCore.Http - open System.Text.Json + open SpanJson let private extra = { @@ -72,32 +48,24 @@ module HttpHandlers = message = "Additional fortune added at request time." } - let rec private renderFortunes (ctx: HttpContext) (dbFortunes: Fortune seq) = - let data = dbFortunes.AsList() + let rec private renderFortunes (ctx: HttpContext) (data: ResizeArray) = data.Add extra data.Sort FortuneComparer - data |> HtmlViews.fortunes |> ctx.WriteHtmlView + data |> HtmlViews.fortunes |> ctx.WriteHtmlViewChunked - let private fortunes : EndpointHandler = + let fortunes : EndpointHandler = fun ctx -> task { - use conn = new NpgsqlConnection(ConnectionString) - let! dbFortunes = conn.QueryAsync("SELECT id, message FROM fortune") + let! dbFortunes = loadFortunes () return! renderFortunes ctx dbFortunes } - [] - [] - type World = { - id: int - randomnumber: int - } - - let private readSingleRow (conn: NpgsqlConnection) = - conn.QueryFirstOrDefaultAsync( - "SELECT id, randomnumber FROM world WHERE id = @Id", - {| Id = Random.Shared.Next(1, 10001) |} - ) + let singleQuery : EndpointHandler = + fun ctx -> + task { + let! result = loadSingleRow() + return! ctx.WriteJsonChunked result + } let private parseQueries (ctx: HttpContext) = match ctx.TryGetRouteValue("count") with @@ -107,66 +75,19 @@ module HttpHandlers = | _, _ -> 1 | _ -> 1 - let private singleQuery : EndpointHandler = - fun ctx -> - task { - use conn = new NpgsqlConnection(ConnectionString) - let! result = readSingleRow conn - return! ctx.WriteJsonChunked result - } - - let private multipleQueries : EndpointHandler = + let multipleQueries : EndpointHandler = fun ctx -> let count = parseQueries ctx - let results = Array.zeroCreate count task { - use conn = new NpgsqlConnection(ConnectionString) - do! conn.OpenAsync() - for i in 0..results.Length-1 do - let! result = readSingleRow conn - results[i] <- result + let! results = loadMultipleRows count return! ctx.WriteJsonChunked results } - let private maxBatch = 500 - let private queries = Array.zeroCreate (maxBatch + 1) - - let private batchUpdateString batchSize = - match queries[batchSize] with - | null -> - let lastIndex = batchSize - 1 - let sb = StringBuilder() - 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() - queries[batchSize] <- result - result - | q -> - q - - let private generateParameters (results: World[]) = - let parameters = Dictionary() - for i in 0..results.Length-1 do - let randomNumber = Random.Shared.Next(1, 10001) - parameters[$"@Rn_{i}"] <- randomNumber - parameters[$"@Id_{i}"] <- results[i].id - results[i] <- { results[i] with randomnumber = randomNumber } - parameters - - let private multipleUpdates : EndpointHandler = + let multipleUpdates : EndpointHandler = fun ctx -> let count = parseQueries ctx - let results = Array.zeroCreate count task { - use conn = new NpgsqlConnection(ConnectionString) - do! conn.OpenAsync() - for i in 0..results.Length-1 do - let! result = readSingleRow conn - results[i] <- result - let parameters = generateParameters results - let! _ = conn.ExecuteAsync(batchUpdateString count, parameters) + let! results = doMultipleUpdates count return! ctx.WriteJsonChunked results } @@ -177,9 +98,9 @@ module HttpHandlers = ctx.WriteBytes(result) let jsonSimple value : EndpointHandler = - let options = JsonSerializerOptions(JsonSerializerDefaults.Web) fun ctx -> - ctx.Response.WriteAsJsonAsync(value, options) + ctx.SetContentType("application/json") + JsonSerializer.Generic.Utf8.SerializeAsync<_>(value, stream = ctx.Response.Body).AsTask() let endpoints = [| diff --git a/frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs b/frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs new file mode 100644 index 00000000000..003fc09d8ce --- /dev/null +++ b/frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs @@ -0,0 +1,24 @@ +module RenderHelpers + + open System.Text + open Oxpecker.ViewEngine + + let prerender (view: HtmlElement -> HtmlElement) = + let sb = StringBuilder() + let mutable head = "" + let fakeHole = + { new HtmlElement with + member this.Render(sb) = + head <- sb.ToString() + 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 diff --git a/frameworks/Go/chi/chi-gojay-prefork.dockerfile b/frameworks/Go/chi/chi-gojay-prefork.dockerfile index 2403792062f..5c9eec667f7 100644 --- a/frameworks/Go/chi/chi-gojay-prefork.dockerfile +++ b/frameworks/Go/chi/chi-gojay-prefork.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:1.23.1 ADD ./src/chi-gojay /chi WORKDIR /chi diff --git a/frameworks/Go/chi/chi-gojay.dockerfile b/frameworks/Go/chi/chi-gojay.dockerfile index 4f4e2ccf72d..c9a6de386d5 100644 --- a/frameworks/Go/chi/chi-gojay.dockerfile +++ b/frameworks/Go/chi/chi-gojay.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:1.23.1 ADD ./src/chi-gojay /chi WORKDIR /chi diff --git a/frameworks/Go/chi/chi-prefork.dockerfile b/frameworks/Go/chi/chi-prefork.dockerfile index a516efa3726..fb363d7b06a 100644 --- a/frameworks/Go/chi/chi-prefork.dockerfile +++ b/frameworks/Go/chi/chi-prefork.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:1.23.1 ADD ./src/chi /chi WORKDIR /chi diff --git a/frameworks/Go/chi/chi-scratch.dockerfile b/frameworks/Go/chi/chi-scratch.dockerfile index 63f9b7ac031..2361e658ca7 100644 --- a/frameworks/Go/chi/chi-scratch.dockerfile +++ b/frameworks/Go/chi/chi-scratch.dockerfile @@ -1,13 +1,12 @@ # build layer -FROM docker.io/golang:1.19-alpine as builder +FROM docker.io/golang:1.23.1-alpine as builder ADD ./src/chi /chi WORKDIR /chi RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 \ - go build -ldflags="-w -s" -o server - -RUN apk --no-cache add --update ca-certificates + go build -ldflags="-w -s" -o server && \ + apk --no-cache add --update ca-certificates # release layer FROM scratch diff --git a/frameworks/Go/chi/chi-sjson-prefork.dockerfile b/frameworks/Go/chi/chi-sjson-prefork.dockerfile index 1e6e7029c01..97071fe5e22 100644 --- a/frameworks/Go/chi/chi-sjson-prefork.dockerfile +++ b/frameworks/Go/chi/chi-sjson-prefork.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:1.23.1 ADD ./src/chi-sjson /chi WORKDIR /chi diff --git a/frameworks/Go/chi/chi-sjson.dockerfile b/frameworks/Go/chi/chi-sjson.dockerfile index 6af99dc4011..99a2204147f 100644 --- a/frameworks/Go/chi/chi-sjson.dockerfile +++ b/frameworks/Go/chi/chi-sjson.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:1.23.1 ADD ./src/chi-sjson /chi WORKDIR /chi diff --git a/frameworks/Go/chi/chi.dockerfile b/frameworks/Go/chi/chi.dockerfile index 205c2bdf7d5..6facc00990f 100644 --- a/frameworks/Go/chi/chi.dockerfile +++ b/frameworks/Go/chi/chi.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:1.23.1 ADD ./src/chi /chi WORKDIR /chi diff --git a/frameworks/Go/chi/src/chi-gojay/go.mod b/frameworks/Go/chi/src/chi-gojay/go.mod index 8521c5e7b7f..f8c266996ec 100644 --- a/frameworks/Go/chi/src/chi-gojay/go.mod +++ b/frameworks/Go/chi/src/chi-gojay/go.mod @@ -1,8 +1,10 @@ module chi/server -go 1.19 +go 1.23.1 require ( - github.com/go-chi/chi/v5 v5.0.7 - github.com/go-sql-driver/mysql v1.6.0 + github.com/go-chi/chi/v5 v5.1.0 + github.com/go-sql-driver/mysql v1.8.1 ) + +require filippo.io/edwards25519 v1.1.0 // indirect diff --git a/frameworks/Go/chi/src/chi-gojay/go.sum b/frameworks/Go/chi/src/chi-gojay/go.sum index 68a8dd38f73..701d0184cbb 100644 --- a/frameworks/Go/chi/src/chi-gojay/go.sum +++ b/frameworks/Go/chi/src/chi-gojay/go.sum @@ -1,4 +1,6 @@ -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= diff --git a/frameworks/Go/chi/src/chi-sjson/go.mod b/frameworks/Go/chi/src/chi-sjson/go.mod index 8521c5e7b7f..f8c266996ec 100644 --- a/frameworks/Go/chi/src/chi-sjson/go.mod +++ b/frameworks/Go/chi/src/chi-sjson/go.mod @@ -1,8 +1,10 @@ module chi/server -go 1.19 +go 1.23.1 require ( - github.com/go-chi/chi/v5 v5.0.7 - github.com/go-sql-driver/mysql v1.6.0 + github.com/go-chi/chi/v5 v5.1.0 + github.com/go-sql-driver/mysql v1.8.1 ) + +require filippo.io/edwards25519 v1.1.0 // indirect diff --git a/frameworks/Go/chi/src/chi-sjson/go.sum b/frameworks/Go/chi/src/chi-sjson/go.sum index 68a8dd38f73..701d0184cbb 100644 --- a/frameworks/Go/chi/src/chi-sjson/go.sum +++ b/frameworks/Go/chi/src/chi-sjson/go.sum @@ -1,4 +1,6 @@ -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= diff --git a/frameworks/Go/chi/src/chi/go.mod b/frameworks/Go/chi/src/chi/go.mod index 829aae200bd..906b029b388 100644 --- a/frameworks/Go/chi/src/chi/go.mod +++ b/frameworks/Go/chi/src/chi/go.mod @@ -1,11 +1,14 @@ module chi/server -go 1.19 +go 1.23.1 require ( - github.com/go-chi/chi/v5 v5.0.7 - github.com/go-sql-driver/mysql v1.6.0 + github.com/go-chi/chi/v5 v5.1.0 + github.com/go-sql-driver/mysql v1.8.1 github.com/mailru/easyjson v0.7.7 ) -require github.com/josharian/intern v1.0.0 // indirect +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect +) diff --git a/frameworks/Go/chi/src/chi/go.sum b/frameworks/Go/chi/src/chi/go.sum index f18160ebd73..b39d37edbe5 100644 --- a/frameworks/Go/chi/src/chi/go.sum +++ b/frameworks/Go/chi/src/chi/go.sum @@ -1,7 +1,9 @@ -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= diff --git a/frameworks/Go/echo/echo.dockerfile b/frameworks/Go/echo/echo.dockerfile index ccc8c0186d2..ab6a616d4d9 100644 --- a/frameworks/Go/echo/echo.dockerfile +++ b/frameworks/Go/echo/echo.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:1.22 ADD ./src /echo WORKDIR /echo diff --git a/frameworks/Go/echo/src/go.mod b/frameworks/Go/echo/src/go.mod index 63fe7fd1cef..04c940547d5 100644 --- a/frameworks/Go/echo/src/go.mod +++ b/frameworks/Go/echo/src/go.mod @@ -1,13 +1,16 @@ module echo/app -go 1.19 +go 1.22 require ( + github.com/jackc/pgx/v5 v5.6.0 github.com/labstack/echo/v4 v4.9.0 - github.com/lib/pq v1.10.7 ) require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/labstack/gommon v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect @@ -15,6 +18,7 @@ require ( github.com/valyala/fasttemplate v1.2.1 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/net v0.23.0 // indirect + golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/frameworks/Go/echo/src/go.sum b/frameworks/Go/echo/src/go.sum index 8732c64400d..e607732cf1b 100644 --- a/frameworks/Go/echo/src/go.sum +++ b/frameworks/Go/echo/src/go.sum @@ -1,12 +1,18 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY= github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -16,8 +22,9 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= @@ -26,6 +33,8 @@ golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +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/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -36,5 +45,5 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/frameworks/Go/echo/src/main.go b/frameworks/Go/echo/src/main.go index ed999f215c0..4642ee2f5b1 100644 --- a/frameworks/Go/echo/src/main.go +++ b/frameworks/Go/echo/src/main.go @@ -1,9 +1,11 @@ package main import ( - "database/sql" + "context" "encoding/json" "fmt" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" "html/template" "io" "log" @@ -13,7 +15,6 @@ import ( "strconv" "github.com/labstack/echo/v4" - _ "github.com/lib/pq" ) type ( @@ -79,10 +80,7 @@ const ( var ( // Database - db *sql.DB - worldSelectStmt *sql.Stmt - worldUpdateStmt *sql.Stmt - fortuneSelectStmt *sql.Stmt + db *pgxpool.Pool // Template Template = &StdTemplate{ @@ -120,8 +118,9 @@ func (h *handler) json() echo.HandlerFunc { // Test 2: Single database query func (h *handler) db() echo.HandlerFunc { return func(c echo.Context) error { + ctx := c.Request().Context() world := new(World) - if err := fetchRandomWorld(world); err != nil { + if err := fetchRandomWorld(ctx, world); err != nil { return err } @@ -134,10 +133,11 @@ func (h *handler) db() echo.HandlerFunc { // Test 3: Multiple database queries func (h *handler) queries() echo.HandlerFunc { return func(c echo.Context) error { + ctx := c.Request().Context() n := getQueryCount(c.QueryParam("n")) worlds := make([]World, n) for i := 0; i < n; i++ { - if err := fetchRandomWorld(&worlds[i]); err != nil { + if err := fetchRandomWorld(ctx, &worlds[i]); err != nil { return err } } @@ -151,7 +151,8 @@ func (h *handler) queries() echo.HandlerFunc { // Test 4: Fortunes func (h *handler) fortunes() echo.HandlerFunc { return func(c echo.Context) error { - rows, err := fortuneSelectStmt.Query() + ctx := c.Request().Context() + rows, err := db.Query(ctx, fortuneSelect) if err != nil { return fmt.Errorf("Error preparing statement: %v", err) } @@ -172,18 +173,19 @@ func (h *handler) fortunes() echo.HandlerFunc { // Test 5: Database updates func (h *handler) updates() echo.HandlerFunc { return func(c echo.Context) error { + ctx := c.Request().Context() n := getQueryCount(c.QueryParam("n")) worlds := make([]World, n) for i := 0; i < n; i++ { // Fetch and modify w := &worlds[i] - if err := fetchRandomWorld(&worlds[i]); err != nil { + if err := fetchRandomWorld(ctx, &worlds[i]); err != nil { return err } w.RandomNumber = uint16(randomWorldNum()) // Update - if _, err := worldUpdateStmt.Exec(w.RandomNumber, w.ID); err != nil { + if _, err := db.Exec(ctx, worldUpdate, w.RandomNumber, w.ID); err != nil { return fmt.Errorf("Error updating world row: %v", err) } } @@ -221,9 +223,9 @@ func (h *handler) plaintext() echo.HandlerFunc { } } -func fetchRandomWorld(w *World) error { +func fetchRandomWorld(ctx context.Context, w *World) error { n := randomWorldNum() - return worldSelectStmt.QueryRow(n).Scan(&w.ID, &w.RandomNumber) + return db.QueryRow(ctx, worldSelect, n).Scan(&w.ID, &w.RandomNumber) } func randomWorldNum() int { @@ -241,7 +243,7 @@ func getQueryCount(q string) int { return n } -func fetchFortunes(rows *sql.Rows) (Fortunes, error) { +func fetchFortunes(rows pgx.Rows) (Fortunes, error) { fortunes := make(Fortunes, 0) for rows.Next() { // Fetch rows f := new(Fortune) @@ -267,24 +269,17 @@ func InitPostgres() { host := "tfb-database" var err error - db, err = sql.Open("postgres", fmt.Sprintf(connectionString, host)) - if err != nil { - log.Fatalf("Error opening database: %v", err) - } - db.SetMaxIdleConns(maxConnections) - db.SetMaxOpenConns(maxConnections) - worldSelectStmt, err = db.Prepare(worldSelect) + dbCfg, err := pgxpool.ParseConfig(fmt.Sprintf(connectionString, host)) if err != nil { - log.Fatal(err) + log.Fatalf("Error reading database connection string: %v", err) } - worldUpdateStmt, err = db.Prepare(worldUpdate) - if err != nil { - log.Fatal(err) - } - fortuneSelectStmt, err = db.Prepare(fortuneSelect) + dbCfg.MaxConns = maxConnections + dbCfg.MinConns = maxConnections + + db, err = pgxpool.NewWithConfig(context.Background(), dbCfg) if err != nil { - log.Fatal(err) + log.Fatalf("Error opening database: %v", err) } } diff --git a/frameworks/Go/fiber/fiber-prefork.dockerfile b/frameworks/Go/fiber/fiber-prefork.dockerfile index 85acc26ef6b..43995ca7de7 100644 --- a/frameworks/Go/fiber/fiber-prefork.dockerfile +++ b/frameworks/Go/fiber/fiber-prefork.dockerfile @@ -1,14 +1,18 @@ -FROM docker.io/golang:1.20 +FROM golang:1.23-alpine as builder WORKDIR /fiber COPY ./src /fiber -RUN go mod download +RUN go mod download && \ + go generate -x ./templates && \ + GOAMD64=v3 go build -ldflags="-s -w" -o app . -RUN go generate -x ./templates +FROM alpine:latest -RUN GOAMD64=v3 go build -ldflags="-s -w" -o app . +WORKDIR /fiber + +COPY --from=builder /fiber/app . EXPOSE 8080 diff --git a/frameworks/Go/fiber/fiber.dockerfile b/frameworks/Go/fiber/fiber.dockerfile index 37f02a59405..38f97b03d56 100644 --- a/frameworks/Go/fiber/fiber.dockerfile +++ b/frameworks/Go/fiber/fiber.dockerfile @@ -1,14 +1,18 @@ -FROM docker.io/golang:1.20 +FROM golang:1.23-alpine as builder WORKDIR /fiber COPY ./src /fiber -RUN go mod download +RUN go mod download && \ + go generate -x ./templates && \ + GOAMD64=v3 go build -ldflags="-s -w" -o app . -RUN go generate -x ./templates +FROM alpine:latest -RUN GOAMD64=v3 go build -ldflags="-s -w" -o app . +WORKDIR /fiber + +COPY --from=builder /fiber/app . EXPOSE 8080 diff --git a/frameworks/Go/fiber/src/go.mod b/frameworks/Go/fiber/src/go.mod index 2372b8f57f7..36e65622cc4 100644 --- a/frameworks/Go/fiber/src/go.mod +++ b/frameworks/Go/fiber/src/go.mod @@ -1,30 +1,30 @@ module fiber/app -go 1.19 +go 1.23 require ( - github.com/goccy/go-json v0.10.0 - github.com/gofiber/fiber/v2 v2.52.1 - github.com/jackc/pgx/v5 v5.5.4 - github.com/valyala/quicktemplate v1.7.0 + github.com/goccy/go-json v0.10.3 + github.com/gofiber/fiber/v2 v2.52.5 + github.com/jackc/pgx/v5 v5.7.1 + github.com/valyala/quicktemplate v1.8.0 ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/fasthttp v1.55.0 // indirect github.com/valyala/tcplisten v1.0.0 // 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.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 ) diff --git a/frameworks/Go/fiber/src/go.sum b/frameworks/Go/fiber/src/go.sum index 5ae0d5e53e3..62741e5425b 100644 --- a/frameworks/Go/fiber/src/go.sum +++ b/frameworks/Go/fiber/src/go.sum @@ -1,72 +1,60 @@ -github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlgHI= -github.com/gofiber/fiber/v2 v2.52.1/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= +github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= -github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= -github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= -github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= -github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM= -github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= +github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= +github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= +github.com/valyala/quicktemplate v1.8.0 h1:zU0tjbIqTRgKQzFY1L42zq0qR3eh4WoQQdIdqCysW5k= +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.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -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/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -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/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/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.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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= 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= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/frameworks/Go/gnet/benchmark_config.json b/frameworks/Go/gnet/benchmark_config.json index 484ec41b8ff..1ff8439be6d 100644 --- a/frameworks/Go/gnet/benchmark_config.json +++ b/frameworks/Go/gnet/benchmark_config.json @@ -20,4 +20,4 @@ "versus": "go" } }] -} \ No newline at end of file +} diff --git a/frameworks/Go/gnet/gnet.dockerfile b/frameworks/Go/gnet/gnet.dockerfile index 2b060d96acd..bf9715306d8 100644 --- a/frameworks/Go/gnet/gnet.dockerfile +++ b/frameworks/Go/gnet/gnet.dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/golang:1.19 +FROM docker.io/golang:latest WORKDIR /gnet diff --git a/frameworks/Go/gnet/src/go.mod b/frameworks/Go/gnet/src/go.mod index bc59a7c9cfd..77b8811ce55 100644 --- a/frameworks/Go/gnet/src/go.mod +++ b/frameworks/Go/gnet/src/go.mod @@ -1,17 +1,19 @@ module gnet -go 1.19 +go 1.23.1 require ( - github.com/panjf2000/gnet/v2 v2.1.2 - go.uber.org/multierr v1.8.0 // indirect - golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 // indirect + github.com/evanphx/wildcat v0.0.0-20141114174135-e7012f664567 + github.com/panjf2000/gnet/v2 v2.5.7 ) require ( - github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/zap v1.23.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/frameworks/Go/gnet/src/go.sum b/frameworks/Go/gnet/src/go.sum index f9f9c868493..e39645c8777 100644 --- a/frameworks/Go/gnet/src/go.sum +++ b/frameworks/Go/gnet/src/go.sum @@ -1,46 +1,39 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/evanphx/wildcat v0.0.0-20141114174135-e7012f664567 h1:7+oQw6YjB/kk9x27AEC7DMXudqERHD583hZpno18lRw= +github.com/evanphx/wildcat v0.0.0-20141114174135-e7012f664567/go.mod h1:XNGflD53X+hfdCAt1NGeBUgiUpe9QmweW/zI1gV26Zw= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/panjf2000/ants/v2 v2.4.8 h1:JgTbolX6K6RreZ4+bfctI0Ifs+3mrE5BIHudQxUDQ9k= -github.com/panjf2000/ants/v2 v2.4.8/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= -github.com/panjf2000/gnet/v2 v2.1.2 h1:WJ/PkbfV6G0wcGOng2pyCwv8oadKiqtP8p+38smN7ao= -github.com/panjf2000/gnet/v2 v2.1.2/go.mod h1:unWr2B4jF0DQPJH3GsXBGQiDcAamM6+Pf5FiK705kc4= +github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8= +github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= +github.com/panjf2000/gnet/v2 v2.5.7 h1:EGGIfLYEVAp2l5WSYT2XddSjpQ642PjwphbWhcJ0WBY= +github.com/panjf2000/gnet/v2 v2.5.7/go.mod h1:ppopMJ8VrDbJu8kDsqFQTgNmpMS8Le5CmPxISf+Sauk= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a h1:lUVfiMMY/te9icPKBqOKkBIMZNxSpM90dxokDeCcfBg= +github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a/go.mod h1:KUxJS71XlMs+ztT+RzsLRoWUQRUpECo/+Rb0EBk8/Wc= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -51,14 +44,15 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3BFmW1QArPYTtwEM3UXc= -golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -71,12 +65,11 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/frameworks/Go/gnet/src/main.go b/frameworks/Go/gnet/src/main.go index 88f1fba167c..125e12ac10b 100644 --- a/frameworks/Go/gnet/src/main.go +++ b/frameworks/Go/gnet/src/main.go @@ -7,9 +7,11 @@ import ( "fmt" "log" "runtime" + "strconv" "sync/atomic" "time" + "github.com/evanphx/wildcat" "github.com/panjf2000/gnet/v2" ) @@ -22,30 +24,68 @@ type httpServer struct { } type httpCodec struct { - delimiter []byte - buf []byte + parser *wildcat.HTTPParser + contentLength int + buf []byte } -func (hc *httpCodec) appendResponse() { - hc.buf = append(hc.buf, "HTTP/1.1 200 OK\r\nServer: gnet\r\nContent-Type: text/plain\r\nDate: "...) - //hc.buf = time.Now().AppendFormat(hc.buf, "Mon, 02 Jan 2006 15:04:05 GMT") - hc.buf = append(hc.buf, NowTimeFormat()...) - hc.buf = append(hc.buf, "\r\nContent-Length: 13\r\n\r\nHello, World!"...) -} - -var errCRLFNotFound = errors.New("CRLF not found") +var CRLF = []byte("\r\n\r\n") func (hc *httpCodec) parse(data []byte) (int, error) { - if idx := bytes.Index(data, hc.delimiter); idx != -1 { + // Perform a legit HTTP request parsing. + bodyOffset, err := hc.parser.Parse(data) + if err != nil { + return 0, err + } + + // First check if the Content-Length header is present. + contentLength := hc.getContentLength() + if contentLength > -1 { + return bodyOffset + contentLength, nil + } + + // If the Content-Length header is not found, + // we need to find the end of the body section. + if idx := bytes.Index(data, CRLF); idx != -1 { return idx + 4, nil } - return -1, errCRLFNotFound + + return 0, errors.New("invalid http request") +} + +var contentLengthKey = []byte("Content-Length") + +func (hc *httpCodec) getContentLength() int { + if hc.contentLength != -1 { + return hc.contentLength + } + + val := hc.parser.FindHeader(contentLengthKey) + if val != nil { + i, err := strconv.ParseInt(string(val), 10, 0) + if err == nil { + hc.contentLength = int(i) + } + } + + return hc.contentLength +} + +func (hc *httpCodec) resetParser() { + hc.contentLength = -1 } func (hc *httpCodec) reset() { + hc.resetParser() hc.buf = hc.buf[:0] } +func (hc *httpCodec) appendResponse() { + hc.buf = append(hc.buf, "HTTP/1.1 200 OK\r\nServer: gnet\r\nContent-Type: text/plain\r\nDate: "...) + hc.buf = append(hc.buf, NowTimeFormat()...) + hc.buf = append(hc.buf, "\r\nContent-Length: 13\r\n\r\nHello, World!"...) +} + func (hs *httpServer) OnBoot(eng gnet.Engine) gnet.Action { hs.eng = eng log.Printf("echo server with multi-core=%t is listening on %s\n", hs.multicore, hs.addr) @@ -53,18 +93,20 @@ func (hs *httpServer) OnBoot(eng gnet.Engine) gnet.Action { } func (hs *httpServer) OnOpen(c gnet.Conn) ([]byte, gnet.Action) { - c.SetContext(&httpCodec{delimiter: []byte("\r\n\r\n")}) + c.SetContext(&httpCodec{parser: wildcat.NewHTTPParser()}) return nil, gnet.None } func (hs *httpServer) OnTraffic(c gnet.Conn) gnet.Action { - buf, _ := c.Next(-1) hc := c.Context().(*httpCodec) + buf, _ := c.Next(-1) + pipeline: nextOffset, err := hc.parse(buf) if err != nil { goto response } + hc.resetParser() hc.appendResponse() buf = buf[nextOffset:] if len(buf) > 0 { diff --git a/frameworks/Go/goravel/README.md b/frameworks/Go/goravel/README.md new file mode 100644 index 00000000000..f9b0d413503 --- /dev/null +++ b/frameworks/Go/goravel/README.md @@ -0,0 +1,44 @@ +# Goravel Benchmarking Test + +[Goravel](https://www.goravel.dev/) is a web application framework with complete functions and excellent scalability. As a starting scaffolding to help Gopher quickly build their own applications. + +The framework's design is consistent with [Laravel](https://github.com/laravel/laravel), simplifying the learning curve for PHPers. Kudos to Laravel! + +### Test Type Implementation Source Code + +* [JSON](src/gin/app/http/controllers/test_controller.go) +* [PLAINTEXT](src/gin/app/http/controllers/test_controller.go) +* [DB](src/gin/app/http/controllers/test_controller.go) +* [QUERY](src/gin/app/http/controllers/test_controller.go) +* [CACHED QUERY](src/gin/app/http/controllers/test_controller.go) +* [UPDATE](src/gin/app/http/controllers/test_controller.go) +* [FORTUNES](src/gin/app/http/controllers/test_controller.go) + +## Test URLs +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext + +### DB + +http://localhost:8080/db + +### QUERY + +http://localhost:8080/query?q= + +### CACHED QUERY + +http://localhost:8080/cached_query?q= + +### UPDATE + +http://localhost:8080/update?q= + +### FORTUNES + +http://localhost:8080/fortunes diff --git a/frameworks/Go/goravel/benchmark_config.json b/frameworks/Go/goravel/benchmark_config.json new file mode 100644 index 00000000000..bcb01875139 --- /dev/null +++ b/frameworks/Go/goravel/benchmark_config.json @@ -0,0 +1,55 @@ +{ + "framework": "goravel", + "tests": [ + { + "default": { + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?q=", + "fortune_url": "/fortunes", + "cached_query_url": "/cached-worlds?q=", + "update_url": "/update?q=", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "postgres", + "framework": "goravel", + "language": "Go", + "flavor": "None", + "orm": "Full", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Goravel Gin", + "notes": "", + "versus": "go" + }, + "fiber": { + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?q=", + "fortune_url": "/fortunes", + "cached_query_url": "/cached-worlds?q=", + "update_url": "/update?q=", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "postgres", + "framework": "goravel", + "language": "Go", + "flavor": "None", + "orm": "Full", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Goravel Fiber", + "notes": "", + "versus": "go" + } + } + ] +} diff --git a/frameworks/Go/goravel/goravel-fiber.dockerfile b/frameworks/Go/goravel/goravel-fiber.dockerfile new file mode 100644 index 00000000000..91d583dface --- /dev/null +++ b/frameworks/Go/goravel/goravel-fiber.dockerfile @@ -0,0 +1,21 @@ +FROM golang:1.22-alpine + +ENV GO111MODULE=on \ + CGO_ENABLED=0 \ + GOAMD64=v3 \ + GOARCH="amd64" \ + GOOS=linux + +WORKDIR /go/goravel + +COPY ./src/fiber /go/goravel + +RUN go mod tidy + +RUN go generate -x ./templates + +RUN go build -ldflags '-s -w --extldflags "-static"' -o /go/goravel/main + +EXPOSE 8080 + +CMD /go/goravel/main diff --git a/frameworks/Go/goravel/goravel.dockerfile b/frameworks/Go/goravel/goravel.dockerfile new file mode 100644 index 00000000000..2000cbbcf35 --- /dev/null +++ b/frameworks/Go/goravel/goravel.dockerfile @@ -0,0 +1,19 @@ +FROM golang:1.22-alpine + +ENV GO111MODULE=on \ + CGO_ENABLED=0 \ + GOAMD64=v3 \ + GOARCH="amd64" \ + GOOS=linux + +WORKDIR /go/goravel + +COPY ./src/gin /go/goravel + +RUN go mod tidy + +RUN go build -tags="sonic avx" -ldflags '-s -w --extldflags "-static"' -o /go/goravel/main + +EXPOSE 8080 + +CMD /go/goravel/main diff --git a/frameworks/Go/goravel/src/fiber/.env b/frameworks/Go/goravel/src/fiber/.env new file mode 100644 index 00000000000..9ceb9e63dc9 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/.env @@ -0,0 +1 @@ +APP_KEY=abcdefghijklmnopqrstuvwxyz123456 \ No newline at end of file diff --git a/frameworks/Go/goravel/src/fiber/app/http/controllers/test_controller.go b/frameworks/Go/goravel/src/fiber/app/http/controllers/test_controller.go new file mode 100644 index 00000000000..c433f221e7b --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/app/http/controllers/test_controller.go @@ -0,0 +1,126 @@ +package controllers + +import ( + "math/rand/v2" + "sort" + + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + + "goravel/templates" +) + +type TestController struct{} + +func NewTestController() *TestController { + initCache() + return &TestController{} +} + +func (r *TestController) Plaintext(ctx http.Context) http.Response { + Plaintext(ctx, helloworld) + return nil +} + +func (r *TestController) JSON(ctx http.Context) http.Response { + message := acquireMessage() + message.Message = helloworld + + JSON(ctx, &message) + releaseMessage(message) + return nil +} + +func (r *TestController) DB(ctx http.Context) http.Response { + world := acquireWorld() + + world.ID = r.getRand() + _ = facades.Orm().Query().Find(&world) + + JSON(ctx, &world) + releaseWorld(world) + return nil +} + +func (r *TestController) Queries(ctx http.Context) http.Response { + n := r.getN(ctx) + worlds := acquireWorlds()[:n] + + for i := 0; i < n; i++ { + worlds[i].ID = r.getRand() + _ = facades.Orm().Query().Find(&worlds[i]) + } + + JSON(ctx, &worlds) + releaseWorlds(worlds) + return nil +} + +func (r *TestController) Update(ctx http.Context) http.Response { + n := r.getN(ctx) + worlds := acquireWorlds()[:n] + + for i := 0; i < n; i++ { + worlds[i].ID = r.getRand() + _ = facades.Orm().Query().Find(&worlds[i]) + } + + // sorting is required for insert deadlock prevention. + sort.Slice(worlds, func(i, j int) bool { + return worlds[i].ID < worlds[j].ID + }) + + tx, _ := facades.Orm().Query().Begin() + for i := 0; i < n; i++ { + worlds[i].RandomNumber = r.getRand() + _ = tx.Save(&worlds[i]) + } + _ = tx.Commit() + + JSON(ctx, &worlds) + releaseWorlds(worlds) + return nil +} + +func (r *TestController) Fortunes(ctx http.Context) http.Response { + fortunes := make([]templates.Fortune, 0) + _ = facades.Orm().Query().Table("Fortune").Get(&fortunes) + + fortunes = append(fortunes, templates.Fortune{Message: "Additional fortune added at request time."}) + sort.Slice(fortunes, func(i, j int) bool { + return fortunes[i].Message < fortunes[j].Message + }) + + HTML(ctx) + templates.WriteFortunePage(ctx.Response().Writer(), fortunes) + return nil +} + +func (r *TestController) CacheQueries(ctx http.Context) http.Response { + n := r.getN(ctx) + worlds := acquireWorlds()[:n] + cached := facades.Cache().Get("worlds").(Worlds) + + for i := 0; i < n; i++ { + worlds[i] = cached[r.getRand()-1] + } + + JSON(ctx, &worlds) + releaseWorlds(worlds) + return nil +} + +func (r *TestController) getN(ctx http.Context) int { + n := ctx.Request().QueryInt(queryparam) + if n < 1 { + n = 1 + } else if n > 500 { + n = 500 + } + + return n +} + +func (r *TestController) getRand() int32 { + return rand.Int32N(worldcount) + 1 +} diff --git a/frameworks/Go/goravel/src/fiber/app/http/controllers/utils.go b/frameworks/Go/goravel/src/fiber/app/http/controllers/utils.go new file mode 100644 index 00000000000..b125edad3f3 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/app/http/controllers/utils.go @@ -0,0 +1,107 @@ +package controllers + +import ( + "fmt" + "sync" + "unsafe" + + "github.com/bytedance/sonic" + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + + "goravel/app/models" +) + +const ( + queryparam = "q" + helloworld = "Hello, World!" + worldcount = 10000 + contentTypePlain = "text/plain; charset=utf-8" + contentTypeHtml = "text/html; charset=utf-8" + contentTypeJson = "application/json" +) + +type Message struct { + Message string `json:"message"` +} + +type Worlds []models.World + +var messagePool = sync.Pool{ + New: func() any { + return new(Message) + }, +} + +var worldPool = sync.Pool{ + New: func() any { + return new(models.World) + }, +} + +var worldsPool = sync.Pool{ + New: func() any { + return make(Worlds, 0, 500) + }, +} + +func acquireMessage() *Message { + return messagePool.Get().(*Message) +} + +func releaseMessage(m *Message) { + m.Message = "" + messagePool.Put(m) +} + +func acquireWorld() *models.World { + return worldPool.Get().(*models.World) +} + +func releaseWorld(w *models.World) { + w.ID = 0 + w.RandomNumber = 0 + worldPool.Put(w) +} + +func acquireWorlds() Worlds { + return worldsPool.Get().(Worlds) +} + +func releaseWorlds(w Worlds) { + w = w[:0] + worldsPool.Put(w) +} + +func str2bytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} + +func initCache() { + worlds := acquireWorlds() + defer releaseWorlds(worlds) + + if err := facades.Orm().Query().Get(&worlds); err != nil { + panic(fmt.Sprintf("Failed to init cached Worlds: %v", err)) + } + + facades.Cache().Forever("worlds", worlds) +} + +func JSON(ctx http.Context, data any) { + ctx.Response().Header("Server", "Goravel") + ctx.Response().Header("Content-Type", contentTypeJson) + bytes, _ := sonic.Marshal(data) + _, _ = ctx.Response().Writer().Write(bytes) +} + +func Plaintext(ctx http.Context, data string) { + ctx.Response().Header("Server", "Goravel") + ctx.Response().Header("Content-Type", contentTypePlain) + _, _ = ctx.Response().Writer().Write(str2bytes(data)) +} + +func HTML(ctx http.Context) { + ctx.Response().Header("Server", "Goravel") + ctx.Response().Header("Content-Type", contentTypeHtml) +} diff --git a/frameworks/Go/goravel/src/fiber/app/models/world.go b/frameworks/Go/goravel/src/fiber/app/models/world.go new file mode 100644 index 00000000000..da724366273 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/app/models/world.go @@ -0,0 +1,10 @@ +package models + +type World struct { + ID int32 `gorm:"primaryKey" json:"id"` + RandomNumber int32 `gorm:"column:randomnumber" json:"randomNumber"` +} + +func (r *World) TableName() string { + return "World" +} diff --git a/frameworks/Go/goravel/src/fiber/app/providers/route_service_provider.go b/frameworks/Go/goravel/src/fiber/app/providers/route_service_provider.go new file mode 100644 index 00000000000..2e96015a24f --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/app/providers/route_service_provider.go @@ -0,0 +1,16 @@ +package providers + +import ( + "github.com/goravel/framework/contracts/foundation" + + "goravel/routes" +) + +type RouteServiceProvider struct{} + +func (receiver *RouteServiceProvider) Register(app foundation.Application) { +} + +func (receiver *RouteServiceProvider) Boot(app foundation.Application) { + routes.Web() +} diff --git a/frameworks/Go/goravel/src/fiber/config/app.go b/frameworks/Go/goravel/src/fiber/config/app.go new file mode 100644 index 00000000000..3ab05123159 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/config/app.go @@ -0,0 +1,40 @@ +package config + +import ( + "github.com/goravel/fiber" + "github.com/goravel/framework/cache" + "github.com/goravel/framework/console" + "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/database" + "github.com/goravel/framework/facades" + "github.com/goravel/framework/http" + "github.com/goravel/framework/log" + "github.com/goravel/framework/route" + "github.com/goravel/framework/validation" + + "goravel/app/providers" +) + +// Boot Start all init methods of the current folder to bootstrap all config. +func Boot() {} + +func init() { + config := facades.Config() + config.Add("app", map[string]any{ + "name": "Goravel", + "env": "production", + "debug": false, + "key": config.Env("APP_KEY", ""), + "providers": []foundation.ServiceProvider{ + &log.ServiceProvider{}, + &console.ServiceProvider{}, + &database.ServiceProvider{}, + &cache.ServiceProvider{}, + &http.ServiceProvider{}, + &route.ServiceProvider{}, + &validation.ServiceProvider{}, + &providers.RouteServiceProvider{}, + &fiber.ServiceProvider{}, + }, + }) +} diff --git a/frameworks/Go/goravel/src/fiber/config/cache.go b/frameworks/Go/goravel/src/fiber/config/cache.go new file mode 100644 index 00000000000..3c638243c2c --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/config/cache.go @@ -0,0 +1,18 @@ +package config + +import ( + "github.com/goravel/framework/facades" +) + +func init() { + config := facades.Config() + config.Add("cache", map[string]any{ + "default": "memory", + "stores": map[string]any{ + "memory": map[string]any{ + "driver": "memory", + }, + }, + "prefix": "goravel_cache", + }) +} diff --git a/frameworks/Go/goravel/src/fiber/config/database.go b/frameworks/Go/goravel/src/fiber/config/database.go new file mode 100644 index 00000000000..e1b1eb3272a --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/config/database.go @@ -0,0 +1,32 @@ +package config + +import ( + "github.com/goravel/framework/facades" +) + +func init() { + config := facades.Config() + config.Add("database", map[string]any{ + "default": "postgresql", + "connections": map[string]any{ + "postgresql": map[string]any{ + "driver": "postgresql", + "host": config.Env("DB_HOST", "tfb-database"), + "port": config.Env("DB_PORT", 5432), + "database": config.Env("DB_DATABASE", "hello_world"), + "username": config.Env("DB_USERNAME", "benchmarkdbuser"), + "password": config.Env("DB_PASSWORD", "benchmarkdbpass"), + "sslmode": "disable", + "timezone": "UTC", + "prefix": "", + "singular": true, + }, + }, + "pool": map[string]any{ + "max_idle_conns": 100, + "max_open_conns": 2000, + "conn_max_idletime": 3600, + "conn_max_lifetime": 3600, + }, + }) +} diff --git a/frameworks/Go/goravel/src/fiber/config/http.go b/frameworks/Go/goravel/src/fiber/config/http.go new file mode 100644 index 00000000000..3d48d689691 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/config/http.go @@ -0,0 +1,26 @@ +package config + +import ( + fiberfacades "github.com/goravel/fiber/facades" + "github.com/goravel/framework/contracts/route" + "github.com/goravel/framework/facades" +) + +func init() { + config := facades.Config() + config.Add("http", map[string]any{ + "default": "fiber", + "drivers": map[string]any{ + "fiber": map[string]any{ + "prefork": true, + "body_limit": 4096, + "header_limit": 4096, + "route": func() (route.Route, error) { + return fiberfacades.Route("fiber"), nil + }, + }, + }, + "host": "", + "port": "8080", + }) +} diff --git a/frameworks/Go/goravel/src/fiber/config/json.go b/frameworks/Go/goravel/src/fiber/config/json.go new file mode 100644 index 00000000000..2b2e1af3f80 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/config/json.go @@ -0,0 +1,31 @@ +package config + +import ( + "github.com/bytedance/sonic" + "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/facades" +) + +func init() { + facades.App().SetJson(NewJson()) +} + +type Json struct { + marshal func(any) ([]byte, error) + unmarshal func([]byte, any) error +} + +func NewJson() foundation.Json { + return &Json{ + marshal: sonic.Marshal, + unmarshal: sonic.Unmarshal, + } +} + +func (j *Json) Marshal(v any) ([]byte, error) { + return j.marshal(v) +} + +func (j *Json) Unmarshal(data []byte, v any) error { + return j.unmarshal(data, v) +} diff --git a/frameworks/Go/goravel/src/fiber/go.mod b/frameworks/Go/goravel/src/fiber/go.mod new file mode 100644 index 00000000000..b31a4b9ba88 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/go.mod @@ -0,0 +1,191 @@ +module goravel + +go 1.22 + +require ( + github.com/bytedance/sonic v1.11.9 + github.com/goravel/fiber v1.2.1 + github.com/goravel/framework v1.14.1 + github.com/valyala/quicktemplate v1.7.0 +) + +require ( + atomicgo.dev/cursor v0.2.0 // indirect + atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/schedule v0.1.0 // indirect + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute v1.25.1 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/pubsub v1.36.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect + github.com/RichardKnop/machinery/v2 v2.0.13 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aws/aws-sdk-go v1.49.6 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/catppuccin/go v0.2.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/charmbracelet/bubbles v0.18.0 // indirect + github.com/charmbracelet/bubbletea v0.26.3 // indirect + github.com/charmbracelet/huh v0.4.2 // indirect + github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0 // indirect + github.com/charmbracelet/lipgloss v0.11.0 // indirect + github.com/charmbracelet/x/ansi v0.1.1 // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a // indirect + github.com/charmbracelet/x/input v0.1.1 // indirect + github.com/charmbracelet/x/term v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.1.2 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect + github.com/glebarez/go-sqlite v1.22.0 // indirect + github.com/glebarez/sqlite v1.11.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-redsync/redsync/v4 v4.8.1 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/go-stack/stack v1.8.0 // indirect + github.com/gofiber/fiber/v2 v2.52.4 // indirect + 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/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 + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gomodule/redigo v2.0.0+incompatible // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/google/wire v0.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect + github.com/gookit/color v1.5.4 // indirect + github.com/gookit/filter v1.2.1 // indirect + github.com/gookit/goutil v0.6.15 // indirect + github.com/gookit/validate v1.5.2 // indirect + github.com/goravel/file-rotatelogs/v2 v2.4.2 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.4 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/microsoft/go-mssqldb v1.6.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pterm/pterm v0.12.79 // indirect + github.com/rabbitmq/amqp091-go v1.9.0 // indirect + github.com/redis/go-redis/v9 v9.5.3 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rotisserie/eris v0.5.4 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/savioxavier/termlink v1.3.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/urfave/cli/v2 v2.27.2 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.55.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.1 // indirect + github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + go.mongodb.org/mongo-driver v1.7.5 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.171.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/grpc v1.64.1 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/mysql v1.5.6 // indirect + gorm.io/driver/postgres v1.5.7 // indirect + gorm.io/driver/sqlserver v1.5.3 // indirect + gorm.io/gorm v1.25.10 // indirect + gorm.io/plugin/dbresolver v1.5.1 // indirect + modernc.org/libc v1.37.6 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/sqlite v1.28.0 // indirect +) diff --git a/frameworks/Go/goravel/src/fiber/go.sum b/frameworks/Go/goravel/src/fiber/go.sum new file mode 100644 index 00000000000..e036eea01cf --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/go.sum @@ -0,0 +1,1170 @@ +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= +atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM= +cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.10.0/go.mod h1:eNpTrkOy7dCpkNyaSNetMa6udbgecJMd0ZsTJS/cuNo= +cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y= +cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest/adal v0.9.16 h1:P8An8Z9rH1ldbOLdFpxYorgOt2sywL9V24dAwWHPuGc= +github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9Ct2d6sC7ol0/ynxc2pO1cpGUM+f4t5adg= +github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU= +github.com/RichardKnop/machinery/v2 v2.0.13 h1:uo9htg+qNBi7UeUK3jcTBl3vTO/vvLKGaOdCOKePl50= +github.com/RichardKnop/machinery/v2 v2.0.13/go.mod h1:Yc2X/QRm9rRfAjB+93NGR+kSUqtnqqs8kME4L+TKKiw= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/aws/aws-sdk-go v1.37.16/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.49.6 h1:yNldzF5kzLBRvKlKz1S0bkvc2+04R1kt13KfBWQBfFA= +github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= +github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= +github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg= +github.com/bytedance/sonic v1.11.9/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.26.3 h1:iXyGvI+FfOWqkB2V07m1DF3xxQijxjY2j8PqiXYqasg= +github.com/charmbracelet/bubbletea v0.26.3/go.mod h1:bpZHfDHTYJC5g+FBK+ptJRCQotRC+Dhh3AoMxa/2+3Q= +github.com/charmbracelet/huh v0.4.2 h1:5wLkwrA58XDAfEZsJzNQlfJ+K8N9+wYwvR5FOM7jXFM= +github.com/charmbracelet/huh v0.4.2/go.mod h1:g9OXBgtY3zRV4ahnVih9bZE+1yGYN+y2C9Q6L2P+WM0= +github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0 h1:79JTuYRirtyCn9ac6rzPt5AQKtBDFc1gKxpw0wBrI+Y= +github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0/go.mod h1:Zxt9FH6togK9kY71pRJGtmyNkJ1eIWdK1gRaXrS/FKA= +github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= +github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= +github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk= +github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a h1:lOpqe2UvPmlln41DGoii7wlSZ/q8qGIon5JJ8Biu46I= +github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= +github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a h1:k/s6UoOSVynWiw7PlclyGO2VdVs5ZLbMIHiGp4shFZE= +github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a/go.mod h1:YBotIGhfoWhHDlnUpJMkjebGV2pdGRCn1Y4/Nk/vVcU= +github.com/charmbracelet/x/input v0.1.1 h1:YDOJaTUKCqtGnq9PHzx3pkkl4pXDOANUHmhH3DqMtM4= +github.com/charmbracelet/x/input v0.1.1/go.mod h1:jvdTVUnNWj/RD6hjC4FsoB0SeZCJ2ZBkiuFP9zXvZI0= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= +github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dhui/dktest v0.4.1 h1:/w+IWuDXVymg3IrRJCHHOkMK10m9aNVMOyD0X12YVTg= +github.com/dhui/dktest v0.4.1/go.mod h1:DdOqcUpL7vgyP4GlF3X3w7HbSlz8cEQzwewPveYEQbA= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= +github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= +github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= +github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-redsync/redsync/v4 v4.8.1 h1:rq2RvdTI0obznMdxKUWGdmmulo7lS9yCzb8fgDKOlbM= +github.com/go-redsync/redsync/v4 v4.8.1/go.mod h1:LmUAsQuQxhzZAoGY7JS6+dNhNmZyonMZiiEDY9plotM= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= +github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= +github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= +github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8= +github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0= +github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= +github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= +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/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= +github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= +github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= +github.com/golang-module/carbon/v2 v2.3.12 h1:VC1DwN1kBwJkh5MjXmTFryjs5g4CWyoM8HAHffZPX/k= +github.com/golang-module/carbon/v2 v2.3.12/go.mod h1:HNsedGzXGuNciZImYP2OMnpiwq/vhIstR/vn45ib5cI= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gookit/filter v1.2.1 h1:37XivkBm2E5qe1KaGdJ5ZfF5l9NYdGWfLEeQadJD8O4= +github.com/gookit/filter v1.2.1/go.mod h1:rxynQFr793x+XDwnRmJFEb53zDw0Zqx3OD7TXWoR9mQ= +github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo= +github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY= +github.com/gookit/validate v1.5.2 h1:i5I2OQ7WYHFRPRATGu9QarR9snnNHydvwSuHXaRWAV0= +github.com/gookit/validate v1.5.2/go.mod h1:yuPy2WwDlwGRa06fFJ5XIO8QEwhRnTC2LmxmBa5SE14= +github.com/goravel/fiber v1.2.1 h1:+hVmrxDzbT1bF/9bIgYytnROTgF1u+xgiVGM3N0S1E4= +github.com/goravel/fiber v1.2.1/go.mod h1:DB4QvgQ/WBqgXGs1cemhqAHFvj7jtI/Irk0RhHBWKoQ= +github.com/goravel/file-rotatelogs/v2 v2.4.2 h1:g68AzbePXcm0V2CpUMc9j4qVzcDn7+7aoWSjZ51C0m4= +github.com/goravel/file-rotatelogs/v2 v2.4.2/go.mod h1:23VuSW8cBS4ax5cmbV+5AaiLpq25b8UJ96IhbAkdo8I= +github.com/goravel/framework v1.14.1 h1:VcJvzn1ItFQh/rQZO1EMkxKmzDrww1S+OGz4hjZi3UY= +github.com/goravel/framework v1.14.1/go.mod h1:rScDXGQZdoVfyxemNPmijlz/2a+lWNOa4jTuak5GGVg= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= +github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc= +github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.79 h1:lH3yrYMhdpeqX9y5Ep1u7DejyHy7NSQg9qrBjF9dFT4= +github.com/pterm/pterm v0.12.79/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo= +github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo= +github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= +github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps= +github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= +github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rotisserie/eris v0.5.4 h1:Il6IvLdAapsMhvuOahHWiBnl1G++Q0/L5UIkI5mARSk= +github.com/rotisserie/eris v0.5.4/go.mod h1:Z/kgYTJiJtocxCbFfvRmO+QejApzG6zpyky9G1A4g9s= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/savioxavier/termlink v1.3.0 h1:3Gl4FzQjUyiHzmoEDfmWEhgIwDiJY4poOQHP+k8ReA4= +github.com/savioxavier/termlink v1.3.0/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= +github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= +github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= +github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= +github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM= +github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= +go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= +go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.39.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= +google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= +gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= +gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= +gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0= +gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/plugin/dbresolver v1.5.1 h1:s9Dj9f7r+1rE3nx/Ywzc85nXptUEaeOO0pt27xdopM8= +gorm.io/plugin/dbresolver v1.5.1/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw= +modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/frameworks/Go/goravel/src/fiber/main.go b/frameworks/Go/goravel/src/fiber/main.go new file mode 100644 index 00000000000..ddd89a4e1a1 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/goravel/framework/facades" + "github.com/goravel/framework/foundation" + + "goravel/config" +) + +func main() { + app := foundation.NewApplication() + + // Bootstrap the application + app.Boot() + + // Bootstrap the config. + config.Boot() + + // Start HTTP server by facades.Route(). + go func() { + if err := facades.Route().Run(); err != nil { + facades.Log().Errorf("Route run error: %v", err) + } + }() + + select {} +} diff --git a/frameworks/Go/goravel/src/fiber/routes/web.go b/frameworks/Go/goravel/src/fiber/routes/web.go new file mode 100644 index 00000000000..86bfbccc2d4 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/routes/web.go @@ -0,0 +1,19 @@ +package routes + +import ( + "github.com/goravel/framework/facades" + + "goravel/app/http/controllers" +) + +func Web() { + testController := controllers.NewTestController() + facades.Route().Get("/plaintext", testController.Plaintext) + facades.Route().Get("/json", testController.JSON) + facades.Route().Get("/db", testController.DB) + facades.Route().Get("/queries", testController.Queries) + facades.Route().Get("/update", testController.Update) + facades.Route().Get("/fortunes", testController.Fortunes) + facades.Route().Get("/cached-worlds", testController.CacheQueries) + +} diff --git a/frameworks/Go/goravel/src/fiber/templates/fortune.go b/frameworks/Go/goravel/src/fiber/templates/fortune.go new file mode 100644 index 00000000000..1d41ca2c80d --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/templates/fortune.go @@ -0,0 +1,53 @@ +package templates + +import ( + "sync" +) + +type Fortune struct { + ID int `json:"id,omitempty"` + Message string `json:"message,omitempty"` +} + +type Fortunes struct { + F []Fortune +} + +//go:generate go run github.com/valyala/quicktemplate/qtc + +var fortunePool = &sync.Pool{ + New: func() interface{} { + return new(Fortune) + }, +} + +var fortunesPool = &sync.Pool{ + New: func() interface{} { + return &Fortunes{ + F: make([]Fortune, 0), + } + }, +} + +// AcquireFortune returns new message from pool. +func AcquireFortune() *Fortune { + return fortunePool.Get().(*Fortune) +} + +// ReleaseFortune resets the message and return it to the pool. +func ReleaseFortune(f *Fortune) { + f.ID = 0 + f.Message = "" + fortunePool.Put(f) +} + +// AcquireFortunes returns new fortunes from pool. +func AcquireFortunes() *Fortunes { + return fortunesPool.Get().(*Fortunes) +} + +// ReleaseFortunes resets the fortunes and return it to the pool. +func ReleaseFortunes(f *Fortunes) { + f.F = f.F[:0] + fortunesPool.Put(f) +} diff --git a/frameworks/Go/goravel/src/fiber/templates/fortune.qtpl b/frameworks/Go/goravel/src/fiber/templates/fortune.qtpl new file mode 100644 index 00000000000..d387990d4ae --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/templates/fortune.qtpl @@ -0,0 +1,15 @@ +{% func FortunePage(rows []Fortune) %} + + +Fortunes + + + + +{% for _, r := range rows %} + +{% endfor %} +
idmessage
{%d int(r.ID) %}{%s r.Message %}
+ + +{% endfunc %} diff --git a/frameworks/Go/goravel/src/fiber/templates/fortune.qtpl.go b/frameworks/Go/goravel/src/fiber/templates/fortune.qtpl.go new file mode 100644 index 00000000000..8af0bbf4f10 --- /dev/null +++ b/frameworks/Go/goravel/src/fiber/templates/fortune.qtpl.go @@ -0,0 +1,81 @@ +// Code generated by qtc from "fortune.qtpl". DO NOT EDIT. +// See https://github.com/valyala/quicktemplate for details. + +//line fortune.qtpl:1 +package templates + +//line fortune.qtpl:1 +import ( + qtio422016 "io" + + qt422016 "github.com/valyala/quicktemplate" +) + +//line fortune.qtpl:1 +var ( + _ = qtio422016.Copy + _ = qt422016.AcquireByteBuffer +) + +//line fortune.qtpl:1 +func StreamFortunePage(qw422016 *qt422016.Writer, rows []Fortune) { +//line fortune.qtpl:1 + qw422016.N().S(` + + +Fortunes + + + + +`) +//line fortune.qtpl:9 + for _, r := range rows { +//line fortune.qtpl:9 + qw422016.N().S(` + +`) +//line fortune.qtpl:11 + } +//line fortune.qtpl:11 + qw422016.N().S(` +
idmessage
`) +//line fortune.qtpl:10 + qw422016.N().D(int(r.ID)) +//line fortune.qtpl:10 + qw422016.N().S(``) +//line fortune.qtpl:10 + qw422016.E().S(r.Message) +//line fortune.qtpl:10 + qw422016.N().S(`
+ + +`) +//line fortune.qtpl:15 +} + +//line fortune.qtpl:15 +func WriteFortunePage(qq422016 qtio422016.Writer, rows []Fortune) { +//line fortune.qtpl:15 + qw422016 := qt422016.AcquireWriter(qq422016) +//line fortune.qtpl:15 + StreamFortunePage(qw422016, rows) +//line fortune.qtpl:15 + qt422016.ReleaseWriter(qw422016) +//line fortune.qtpl:15 +} + +//line fortune.qtpl:15 +func FortunePage(rows []Fortune) string { +//line fortune.qtpl:15 + qb422016 := qt422016.AcquireByteBuffer() +//line fortune.qtpl:15 + WriteFortunePage(qb422016, rows) +//line fortune.qtpl:15 + qs422016 := string(qb422016.B) +//line fortune.qtpl:15 + qt422016.ReleaseByteBuffer(qb422016) +//line fortune.qtpl:15 + return qs422016 +//line fortune.qtpl:15 +} diff --git a/frameworks/Go/goravel/src/gin/.env b/frameworks/Go/goravel/src/gin/.env new file mode 100644 index 00000000000..9ceb9e63dc9 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/.env @@ -0,0 +1 @@ +APP_KEY=abcdefghijklmnopqrstuvwxyz123456 \ No newline at end of file diff --git a/frameworks/Go/goravel/src/gin/app/http/controllers/test_controller.go b/frameworks/Go/goravel/src/gin/app/http/controllers/test_controller.go new file mode 100644 index 00000000000..7b68b61daeb --- /dev/null +++ b/frameworks/Go/goravel/src/gin/app/http/controllers/test_controller.go @@ -0,0 +1,129 @@ +package controllers + +import ( + "math/rand/v2" + "sort" + + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + + "goravel/app/models" +) + +type TestController struct{} + +func NewTestController() *TestController { + initCache() + return &TestController{} +} + +func (r *TestController) Plaintext(ctx http.Context) http.Response { + Plaintext(ctx, helloworld) + return nil +} + +func (r *TestController) JSON(ctx http.Context) http.Response { + message := acquireMessage() + message.Message = helloworld + + JSON(ctx, &message) + releaseMessage(message) + return nil +} + +func (r *TestController) DB(ctx http.Context) http.Response { + world := acquireWorld() + + world.ID = r.getRand() + _ = facades.Orm().Query().Find(&world) + + JSON(ctx, &world) + releaseWorld(world) + return nil +} + +func (r *TestController) Queries(ctx http.Context) http.Response { + n := r.getN(ctx) + worlds := acquireWorlds()[:n] + + for i := 0; i < n; i++ { + worlds[i].ID = r.getRand() + _ = facades.Orm().Query().Find(&worlds[i]) + } + + JSON(ctx, &worlds) + releaseWorlds(worlds) + return nil +} + +func (r *TestController) Update(ctx http.Context) http.Response { + n := r.getN(ctx) + worlds := acquireWorlds()[:n] + + for i := 0; i < n; i++ { + worlds[i].ID = r.getRand() + _ = facades.Orm().Query().Find(&worlds[i]) + } + + // sorting is required for insert deadlock prevention. + sort.Slice(worlds, func(i, j int) bool { + return worlds[i].ID < worlds[j].ID + }) + + tx, _ := facades.Orm().Query().Begin() + for i := 0; i < n; i++ { + worlds[i].RandomNumber = r.getRand() + _ = tx.Save(&worlds[i]) + } + _ = tx.Commit() + + JSON(ctx, &worlds) + releaseWorlds(worlds) + return nil +} + +func (r *TestController) Fortunes(ctx http.Context) http.Response { + fortunes := make([]models.Fortune, 0) + _ = facades.Orm().Query().Get(&fortunes) + fortunes = append(fortunes, models.Fortune{Message: "Additional fortune added at request time."}) + + sort.Slice(fortunes, func(i, j int) bool { + return fortunes[i].Message < fortunes[j].Message + }) + + return ctx.Response(). + Header("Server", "Goravel"). + View(). + Make("fortunes.tmpl", map[string]any{ + "fortunes": fortunes, + }) +} + +func (r *TestController) CacheQueries(ctx http.Context) http.Response { + n := r.getN(ctx) + worlds := acquireWorlds()[:n] + cached := facades.Cache().Get("worlds").(Worlds) + + for i := 0; i < n; i++ { + worlds[i] = cached[r.getRand()-1] + } + + JSON(ctx, &worlds) + releaseWorlds(worlds) + return nil +} + +func (r *TestController) getN(ctx http.Context) int { + n := ctx.Request().QueryInt(queryparam) + if n < 1 { + n = 1 + } else if n > 500 { + n = 500 + } + + return n +} + +func (r *TestController) getRand() int32 { + return rand.Int32N(worldcount) + 1 +} diff --git a/frameworks/Go/goravel/src/gin/app/http/controllers/utils.go b/frameworks/Go/goravel/src/gin/app/http/controllers/utils.go new file mode 100644 index 00000000000..9cac0d34b48 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/app/http/controllers/utils.go @@ -0,0 +1,101 @@ +package controllers + +import ( + "fmt" + "sync" + "unsafe" + + "github.com/bytedance/sonic" + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + + "goravel/app/models" +) + +const ( + queryparam = "q" + helloworld = "Hello, World!" + worldcount = 10000 + contentTypePlain = "text/plain; charset=utf-8" + contentTypeJson = "application/json" +) + +type Message struct { + Message string `json:"message"` +} + +type Worlds []models.World + +var messagePool = sync.Pool{ + New: func() any { + return new(Message) + }, +} + +var worldPool = sync.Pool{ + New: func() any { + return new(models.World) + }, +} + +var worldsPool = sync.Pool{ + New: func() any { + return make(Worlds, 0, 500) + }, +} + +func acquireMessage() *Message { + return messagePool.Get().(*Message) +} + +func releaseMessage(m *Message) { + m.Message = "" + messagePool.Put(m) +} + +func acquireWorld() *models.World { + return worldPool.Get().(*models.World) +} + +func releaseWorld(w *models.World) { + w.ID = 0 + w.RandomNumber = 0 + worldPool.Put(w) +} + +func acquireWorlds() Worlds { + return worldsPool.Get().(Worlds) +} + +func releaseWorlds(w Worlds) { + w = w[:0] + worldsPool.Put(w) +} + +func str2bytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} + +func initCache() { + worlds := acquireWorlds() + defer releaseWorlds(worlds) + + if err := facades.Orm().Query().Get(&worlds); err != nil { + panic(fmt.Sprintf("Failed to init cached Worlds: %v", err)) + } + + facades.Cache().Forever("worlds", worlds) +} + +func JSON(ctx http.Context, data any) { + ctx.Response().Header("Server", "Goravel") + ctx.Response().Header("Content-Type", contentTypeJson) + bytes, _ := sonic.Marshal(data) + _, _ = ctx.Response().Writer().Write(bytes) +} + +func Plaintext(ctx http.Context, data string) { + ctx.Response().Header("Server", "Goravel") + ctx.Response().Header("Content-Type", contentTypePlain) + _, _ = ctx.Response().Writer().Write(str2bytes(data)) +} diff --git a/frameworks/Go/goravel/src/gin/app/models/fortune.go b/frameworks/Go/goravel/src/gin/app/models/fortune.go new file mode 100644 index 00000000000..8e8e1dd21a8 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/app/models/fortune.go @@ -0,0 +1,10 @@ +package models + +type Fortune struct { + ID uint `gorm:"primaryKey" json:"id"` + Message string `gorm:"column:message" json:"message"` +} + +func (r *Fortune) TableName() string { + return "Fortune" +} diff --git a/frameworks/Go/goravel/src/gin/app/models/world.go b/frameworks/Go/goravel/src/gin/app/models/world.go new file mode 100644 index 00000000000..da724366273 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/app/models/world.go @@ -0,0 +1,10 @@ +package models + +type World struct { + ID int32 `gorm:"primaryKey" json:"id"` + RandomNumber int32 `gorm:"column:randomnumber" json:"randomNumber"` +} + +func (r *World) TableName() string { + return "World" +} diff --git a/frameworks/Go/goravel/src/gin/app/providers/route_service_provider.go b/frameworks/Go/goravel/src/gin/app/providers/route_service_provider.go new file mode 100644 index 00000000000..2e96015a24f --- /dev/null +++ b/frameworks/Go/goravel/src/gin/app/providers/route_service_provider.go @@ -0,0 +1,16 @@ +package providers + +import ( + "github.com/goravel/framework/contracts/foundation" + + "goravel/routes" +) + +type RouteServiceProvider struct{} + +func (receiver *RouteServiceProvider) Register(app foundation.Application) { +} + +func (receiver *RouteServiceProvider) Boot(app foundation.Application) { + routes.Web() +} diff --git a/frameworks/Go/goravel/src/gin/config/app.go b/frameworks/Go/goravel/src/gin/config/app.go new file mode 100644 index 00000000000..f6f898c90fd --- /dev/null +++ b/frameworks/Go/goravel/src/gin/config/app.go @@ -0,0 +1,40 @@ +package config + +import ( + "github.com/goravel/framework/cache" + "github.com/goravel/framework/console" + "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/database" + "github.com/goravel/framework/facades" + "github.com/goravel/framework/http" + "github.com/goravel/framework/log" + "github.com/goravel/framework/route" + "github.com/goravel/framework/validation" + "github.com/goravel/gin" + + "goravel/app/providers" +) + +// Boot Start all init methods of the current folder to bootstrap all config. +func Boot() {} + +func init() { + config := facades.Config() + config.Add("app", map[string]any{ + "name": "Goravel", + "env": "production", + "debug": false, + "key": config.Env("APP_KEY", ""), + "providers": []foundation.ServiceProvider{ + &log.ServiceProvider{}, + &console.ServiceProvider{}, + &database.ServiceProvider{}, + &cache.ServiceProvider{}, + &http.ServiceProvider{}, + &route.ServiceProvider{}, + &validation.ServiceProvider{}, + &providers.RouteServiceProvider{}, + &gin.ServiceProvider{}, + }, + }) +} diff --git a/frameworks/Go/goravel/src/gin/config/cache.go b/frameworks/Go/goravel/src/gin/config/cache.go new file mode 100644 index 00000000000..3c638243c2c --- /dev/null +++ b/frameworks/Go/goravel/src/gin/config/cache.go @@ -0,0 +1,18 @@ +package config + +import ( + "github.com/goravel/framework/facades" +) + +func init() { + config := facades.Config() + config.Add("cache", map[string]any{ + "default": "memory", + "stores": map[string]any{ + "memory": map[string]any{ + "driver": "memory", + }, + }, + "prefix": "goravel_cache", + }) +} diff --git a/frameworks/Go/goravel/src/gin/config/database.go b/frameworks/Go/goravel/src/gin/config/database.go new file mode 100644 index 00000000000..e1b1eb3272a --- /dev/null +++ b/frameworks/Go/goravel/src/gin/config/database.go @@ -0,0 +1,32 @@ +package config + +import ( + "github.com/goravel/framework/facades" +) + +func init() { + config := facades.Config() + config.Add("database", map[string]any{ + "default": "postgresql", + "connections": map[string]any{ + "postgresql": map[string]any{ + "driver": "postgresql", + "host": config.Env("DB_HOST", "tfb-database"), + "port": config.Env("DB_PORT", 5432), + "database": config.Env("DB_DATABASE", "hello_world"), + "username": config.Env("DB_USERNAME", "benchmarkdbuser"), + "password": config.Env("DB_PASSWORD", "benchmarkdbpass"), + "sslmode": "disable", + "timezone": "UTC", + "prefix": "", + "singular": true, + }, + }, + "pool": map[string]any{ + "max_idle_conns": 100, + "max_open_conns": 2000, + "conn_max_idletime": 3600, + "conn_max_lifetime": 3600, + }, + }) +} diff --git a/frameworks/Go/goravel/src/gin/config/http.go b/frameworks/Go/goravel/src/gin/config/http.go new file mode 100644 index 00000000000..9d8846ee610 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/config/http.go @@ -0,0 +1,30 @@ +package config + +import ( + "github.com/gin-gonic/gin/render" + "github.com/goravel/framework/contracts/route" + "github.com/goravel/framework/facades" + "github.com/goravel/gin" + ginfacades "github.com/goravel/gin/facades" +) + +func init() { + config := facades.Config() + config.Add("http", map[string]any{ + "default": "gin", + "drivers": map[string]any{ + "gin": map[string]any{ + "body_limit": 4096, + "header_limit": 4096, + "route": func() (route.Route, error) { + return ginfacades.Route("gin"), nil + }, + "template": func() (render.HTMLRender, error) { + return gin.DefaultTemplate() + }, + }, + }, + "host": "", + "port": "8080", + }) +} diff --git a/frameworks/Go/goravel/src/gin/config/json.go b/frameworks/Go/goravel/src/gin/config/json.go new file mode 100644 index 00000000000..2b2e1af3f80 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/config/json.go @@ -0,0 +1,31 @@ +package config + +import ( + "github.com/bytedance/sonic" + "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/facades" +) + +func init() { + facades.App().SetJson(NewJson()) +} + +type Json struct { + marshal func(any) ([]byte, error) + unmarshal func([]byte, any) error +} + +func NewJson() foundation.Json { + return &Json{ + marshal: sonic.Marshal, + unmarshal: sonic.Unmarshal, + } +} + +func (j *Json) Marshal(v any) ([]byte, error) { + return j.marshal(v) +} + +func (j *Json) Unmarshal(data []byte, v any) error { + return j.unmarshal(data, v) +} diff --git a/frameworks/Go/goravel/src/gin/go.mod b/frameworks/Go/goravel/src/gin/go.mod new file mode 100644 index 00000000000..e010a6645c6 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/go.mod @@ -0,0 +1,194 @@ +module goravel + +go 1.22 + +require ( + github.com/bytedance/sonic v1.11.9 + github.com/gin-gonic/gin v1.10.0 + github.com/goravel/framework v1.14.1 + github.com/goravel/gin v1.2.1 +) + +require ( + atomicgo.dev/cursor v0.2.0 // indirect + atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/schedule v0.1.0 // indirect + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute v1.25.1 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/pubsub v1.36.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect + github.com/RichardKnop/machinery/v2 v2.0.13 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aws/aws-sdk-go v1.49.6 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/catppuccin/go v0.2.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/charmbracelet/bubbles v0.18.0 // indirect + github.com/charmbracelet/bubbletea v0.26.3 // indirect + github.com/charmbracelet/huh v0.4.2 // indirect + github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0 // indirect + github.com/charmbracelet/lipgloss v0.11.0 // indirect + github.com/charmbracelet/x/ansi v0.1.1 // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a // indirect + github.com/charmbracelet/x/input v0.1.1 // indirect + github.com/charmbracelet/x/term v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.1.2 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/glebarez/go-sqlite v1.22.0 // indirect + github.com/glebarez/sqlite v1.11.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-redsync/redsync/v4 v4.8.1 // indirect + 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.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 + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gomodule/redigo v2.0.0+incompatible // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/google/wire v0.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect + github.com/gookit/color v1.5.4 // indirect + github.com/gookit/filter v1.2.1 // indirect + github.com/gookit/goutil v0.6.15 // indirect + github.com/gookit/validate v1.5.2 // indirect + github.com/goravel/file-rotatelogs/v2 v2.4.2 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.4 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/microsoft/go-mssqldb v1.6.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pterm/pterm v0.12.79 // indirect + github.com/rabbitmq/amqp091-go v1.9.0 // indirect + github.com/redis/go-redis/v9 v9.5.3 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rotisserie/eris v0.5.4 // indirect + github.com/rs/cors v1.11.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/savioxavier/termlink v1.3.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/unrolled/secure v1.14.0 // indirect + github.com/urfave/cli/v2 v2.27.2 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.1 // indirect + github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + go.mongodb.org/mongo-driver v1.7.5 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.171.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/mysql v1.5.6 // indirect + gorm.io/driver/postgres v1.5.7 // indirect + gorm.io/driver/sqlserver v1.5.3 // indirect + gorm.io/gorm v1.25.10 // indirect + gorm.io/plugin/dbresolver v1.5.1 // indirect + modernc.org/libc v1.37.6 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/sqlite v1.28.0 // indirect +) diff --git a/frameworks/Go/goravel/src/gin/go.sum b/frameworks/Go/goravel/src/gin/go.sum new file mode 100644 index 00000000000..fefa43c5558 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/go.sum @@ -0,0 +1,1171 @@ +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= +atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM= +cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.10.0/go.mod h1:eNpTrkOy7dCpkNyaSNetMa6udbgecJMd0ZsTJS/cuNo= +cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y= +cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest/adal v0.9.16 h1:P8An8Z9rH1ldbOLdFpxYorgOt2sywL9V24dAwWHPuGc= +github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9Ct2d6sC7ol0/ynxc2pO1cpGUM+f4t5adg= +github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU= +github.com/RichardKnop/machinery/v2 v2.0.13 h1:uo9htg+qNBi7UeUK3jcTBl3vTO/vvLKGaOdCOKePl50= +github.com/RichardKnop/machinery/v2 v2.0.13/go.mod h1:Yc2X/QRm9rRfAjB+93NGR+kSUqtnqqs8kME4L+TKKiw= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/aws/aws-sdk-go v1.37.16/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.49.6 h1:yNldzF5kzLBRvKlKz1S0bkvc2+04R1kt13KfBWQBfFA= +github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= +github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= +github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg= +github.com/bytedance/sonic v1.11.9/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.26.3 h1:iXyGvI+FfOWqkB2V07m1DF3xxQijxjY2j8PqiXYqasg= +github.com/charmbracelet/bubbletea v0.26.3/go.mod h1:bpZHfDHTYJC5g+FBK+ptJRCQotRC+Dhh3AoMxa/2+3Q= +github.com/charmbracelet/huh v0.4.2 h1:5wLkwrA58XDAfEZsJzNQlfJ+K8N9+wYwvR5FOM7jXFM= +github.com/charmbracelet/huh v0.4.2/go.mod h1:g9OXBgtY3zRV4ahnVih9bZE+1yGYN+y2C9Q6L2P+WM0= +github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0 h1:79JTuYRirtyCn9ac6rzPt5AQKtBDFc1gKxpw0wBrI+Y= +github.com/charmbracelet/huh/spinner v0.0.0-20240508140610-13957916abf0/go.mod h1:Zxt9FH6togK9kY71pRJGtmyNkJ1eIWdK1gRaXrS/FKA= +github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= +github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= +github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk= +github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a h1:lOpqe2UvPmlln41DGoii7wlSZ/q8qGIon5JJ8Biu46I= +github.com/charmbracelet/x/exp/strings v0.0.0-20240524151031-ff83003bf67a/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= +github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a h1:k/s6UoOSVynWiw7PlclyGO2VdVs5ZLbMIHiGp4shFZE= +github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a/go.mod h1:YBotIGhfoWhHDlnUpJMkjebGV2pdGRCn1Y4/Nk/vVcU= +github.com/charmbracelet/x/input v0.1.1 h1:YDOJaTUKCqtGnq9PHzx3pkkl4pXDOANUHmhH3DqMtM4= +github.com/charmbracelet/x/input v0.1.1/go.mod h1:jvdTVUnNWj/RD6hjC4FsoB0SeZCJ2ZBkiuFP9zXvZI0= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= +github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dhui/dktest v0.4.1 h1:/w+IWuDXVymg3IrRJCHHOkMK10m9aNVMOyD0X12YVTg= +github.com/dhui/dktest v0.4.1/go.mod h1:DdOqcUpL7vgyP4GlF3X3w7HbSlz8cEQzwewPveYEQbA= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= +github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= +github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-redsync/redsync/v4 v4.8.1 h1:rq2RvdTI0obznMdxKUWGdmmulo7lS9yCzb8fgDKOlbM= +github.com/go-redsync/redsync/v4 v4.8.1/go.mod h1:LmUAsQuQxhzZAoGY7JS6+dNhNmZyonMZiiEDY9plotM= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/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= +github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= +github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= +github.com/golang-module/carbon/v2 v2.3.12 h1:VC1DwN1kBwJkh5MjXmTFryjs5g4CWyoM8HAHffZPX/k= +github.com/golang-module/carbon/v2 v2.3.12/go.mod h1:HNsedGzXGuNciZImYP2OMnpiwq/vhIstR/vn45ib5cI= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gookit/filter v1.2.1 h1:37XivkBm2E5qe1KaGdJ5ZfF5l9NYdGWfLEeQadJD8O4= +github.com/gookit/filter v1.2.1/go.mod h1:rxynQFr793x+XDwnRmJFEb53zDw0Zqx3OD7TXWoR9mQ= +github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo= +github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY= +github.com/gookit/validate v1.5.2 h1:i5I2OQ7WYHFRPRATGu9QarR9snnNHydvwSuHXaRWAV0= +github.com/gookit/validate v1.5.2/go.mod h1:yuPy2WwDlwGRa06fFJ5XIO8QEwhRnTC2LmxmBa5SE14= +github.com/goravel/file-rotatelogs/v2 v2.4.2 h1:g68AzbePXcm0V2CpUMc9j4qVzcDn7+7aoWSjZ51C0m4= +github.com/goravel/file-rotatelogs/v2 v2.4.2/go.mod h1:23VuSW8cBS4ax5cmbV+5AaiLpq25b8UJ96IhbAkdo8I= +github.com/goravel/framework v1.14.1 h1:VcJvzn1ItFQh/rQZO1EMkxKmzDrww1S+OGz4hjZi3UY= +github.com/goravel/framework v1.14.1/go.mod h1:rScDXGQZdoVfyxemNPmijlz/2a+lWNOa4jTuak5GGVg= +github.com/goravel/gin v1.2.1 h1:lnQX3NKUEaSx8x7AAJpoeVkXgi+MVQ9FXy4QywHQElo= +github.com/goravel/gin v1.2.1/go.mod h1:Qt3NJysg/eoxXL4y/swwFUcfcIT7XG+xb0rWChweZfY= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= +github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc= +github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.79 h1:lH3yrYMhdpeqX9y5Ep1u7DejyHy7NSQg9qrBjF9dFT4= +github.com/pterm/pterm v0.12.79/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo= +github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo= +github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= +github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps= +github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= +github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rotisserie/eris v0.5.4 h1:Il6IvLdAapsMhvuOahHWiBnl1G++Q0/L5UIkI5mARSk= +github.com/rotisserie/eris v0.5.4/go.mod h1:Z/kgYTJiJtocxCbFfvRmO+QejApzG6zpyky9G1A4g9s= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/savioxavier/termlink v1.3.0 h1:3Gl4FzQjUyiHzmoEDfmWEhgIwDiJY4poOQHP+k8ReA4= +github.com/savioxavier/termlink v1.3.0/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/unrolled/secure v1.14.0 h1:u9vJTU/pR4Bny0ntLUMxdfLtmIRGvQf2sEFuA0TG9AE= +github.com/unrolled/secure v1.14.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= +github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= +go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= +go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.39.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= +google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= +gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= +gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= +gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0= +gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/plugin/dbresolver v1.5.1 h1:s9Dj9f7r+1rE3nx/Ywzc85nXptUEaeOO0pt27xdopM8= +gorm.io/plugin/dbresolver v1.5.1/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw= +modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/frameworks/Go/goravel/src/gin/main.go b/frameworks/Go/goravel/src/gin/main.go new file mode 100644 index 00000000000..ddd89a4e1a1 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/goravel/framework/facades" + "github.com/goravel/framework/foundation" + + "goravel/config" +) + +func main() { + app := foundation.NewApplication() + + // Bootstrap the application + app.Boot() + + // Bootstrap the config. + config.Boot() + + // Start HTTP server by facades.Route(). + go func() { + if err := facades.Route().Run(); err != nil { + facades.Log().Errorf("Route run error: %v", err) + } + }() + + select {} +} diff --git a/frameworks/Go/goravel/src/gin/resources/views/fortunes.tmpl b/frameworks/Go/goravel/src/gin/resources/views/fortunes.tmpl new file mode 100644 index 00000000000..4c58fa53da0 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/resources/views/fortunes.tmpl @@ -0,0 +1,22 @@ +{{define "fortunes.tmpl"}} + + + + Fortunes + + + + + + + + {{range .fortunes}} + + + + + {{end}} +
idmessage
{{.ID}}{{.Message}}
+ + +{{end}} diff --git a/frameworks/Go/goravel/src/gin/routes/web.go b/frameworks/Go/goravel/src/gin/routes/web.go new file mode 100644 index 00000000000..86bfbccc2d4 --- /dev/null +++ b/frameworks/Go/goravel/src/gin/routes/web.go @@ -0,0 +1,19 @@ +package routes + +import ( + "github.com/goravel/framework/facades" + + "goravel/app/http/controllers" +) + +func Web() { + testController := controllers.NewTestController() + facades.Route().Get("/plaintext", testController.Plaintext) + facades.Route().Get("/json", testController.JSON) + facades.Route().Get("/db", testController.DB) + facades.Route().Get("/queries", testController.Queries) + facades.Route().Get("/update", testController.Update) + facades.Route().Get("/fortunes", testController.Fortunes) + facades.Route().Get("/cached-worlds", testController.CacheQueries) + +} diff --git a/frameworks/Go/silverlining/benchmark_config.json b/frameworks/Go/silverlining/benchmark_config.json index 368481b04cf..bd8544f9111 100644 --- a/frameworks/Go/silverlining/benchmark_config.json +++ b/frameworks/Go/silverlining/benchmark_config.json @@ -35,7 +35,7 @@ "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "silverlining", + "display_name": "silverlining [prefork]", "notes": "", "versus": "go" } diff --git a/frameworks/Java/armeria/benchmark_config.json b/frameworks/Java/armeria/benchmark_config.json index 32cdbd6303f..bdc474dfd9b 100644 --- a/frameworks/Java/armeria/benchmark_config.json +++ b/frameworks/Java/armeria/benchmark_config.json @@ -7,6 +7,7 @@ "db_url": "/db", "query_url": "/queries/", "update_url": "/updates/", + "fortune_url": "/fortunes", "port": 8080, "approach": "Realistic", "classification": "Micro", diff --git a/frameworks/Java/helidon/nima/pom.xml b/frameworks/Java/helidon/nima/pom.xml index c884659a0a0..c859b758cfc 100644 --- a/frameworks/Java/helidon/nima/pom.xml +++ b/frameworks/Java/helidon/nima/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.3 + 4.1.2 diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java new file mode 100644 index 00000000000..322a7cf030c --- /dev/null +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java @@ -0,0 +1,92 @@ +package io.helidon.benchmark.nima; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.List; + +import com.jsoniter.output.JsonStream; +import com.jsoniter.output.JsonStreamPool; +import com.jsoniter.spi.JsonException; + +public class JsonSerializer { + + private JsonSerializer() { + } + + /** + * Serialize an instance into a JSON object and return it as a byte array. + * + * @param obj the instance + * @return the byte array + */ + public static byte[] serialize(Object obj) { + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + stream.reset(null); + stream.writeVal(obj.getClass(), obj); + return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); + } catch (IOException e) { + throw new JsonException(e); + } finally { + JsonStreamPool.returnJsonStream(stream); + } + } + + /** + * Serialize a map of strings into a JSON object and return it as a byte array. + * + * @param map the map + * @return the byte array + */ + public static byte[] serialize(Map map) { + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + stream.reset(null); + stream.writeObjectStart(); + map.forEach((k, v) -> { + try { + stream.writeObjectField(k); + stream.writeVal(v); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + stream.writeObjectEnd(); + return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); + } catch (IOException e) { + throw new JsonException(e); + } finally { + JsonStreamPool.returnJsonStream(stream); + } + } + + /** + * Serialize a list of objects into a JSON array and return it as a byte array. + * + * @param objs the list of objects + * @return the byte array + */ + public static byte[] serialize(List objs) { + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + stream.reset(null); + stream.writeArrayStart(); + int i = 0; + int n = objs.size(); + for (Object obj : objs) { + stream.writeVal(obj.getClass(), obj); + if (i++ < n - 1) { + stream.writeMore(); + } + + } + stream.writeArrayEnd(); + return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); + } catch (IOException e) { + throw new JsonException(e); + } finally { + JsonStreamPool.returnJsonStream(stream); + } + } +} diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java index 92896867246..df669d8a7a7 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java @@ -16,14 +16,9 @@ package io.helidon.benchmark.nima; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.logging.Logger; -import com.jsoniter.output.JsonStream; -import com.jsoniter.output.JsonStreamPool; -import com.jsoniter.spi.JsonException; import io.helidon.benchmark.nima.models.DbRepository; import io.helidon.benchmark.nima.models.HikariJdbcRepository; import io.helidon.benchmark.nima.models.PgClientRepository; @@ -41,6 +36,8 @@ import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; +import static io.helidon.benchmark.nima.JsonSerializer.serialize; + /** * Main class of the benchmark. * Opens server on localhost:8080 and exposes {@code /plaintext} and {@code /json} endpoints adhering to the @@ -90,29 +87,14 @@ static void routing(HttpRules rules) { rules.get("/plaintext", new PlaintextHandler()) .get("/json", new JsonHandler()) - .get("/10k", new JsonKHandler(10)) .get("/fortunes", new FortuneHandler(repository)) .register("/", new DbService(repository)); } - private static byte[] serializeMsg(Message obj) { - JsonStream stream = JsonStreamPool.borrowJsonStream(); - try { - stream.reset(null); - stream.writeVal(Message.class, obj); - return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); - } catch (IOException e) { - throw new JsonException(e); - } finally { - JsonStreamPool.returnJsonStream(stream); - } - } - static class PlaintextHandler implements Handler { static final Header CONTENT_TYPE = HeaderValues.createCached(HeaderNames.CONTENT_TYPE, - "text/plain; charset=UTF-8"); + "text/plain; charset=UTF-8"); static final Header CONTENT_LENGTH = HeaderValues.createCached(HeaderNames.CONTENT_LENGTH, "13"); - private static final byte[] RESPONSE_BYTES = "Hello, World!".getBytes(StandardCharsets.UTF_8); @Override @@ -126,44 +108,16 @@ public void handle(ServerRequest req, ServerResponse res) { static class JsonHandler implements Handler { private static final String MESSAGE = "Hello, World!"; - private static final int JSON_LENGTH = serializeMsg(new Message(MESSAGE)).length; + private static final int JSON_LENGTH = serialize(new Message(MESSAGE)).length; static final Header CONTENT_LENGTH = HeaderValues.createCached(HeaderNames.CONTENT_LENGTH, - String.valueOf(JSON_LENGTH)); + String.valueOf(JSON_LENGTH)); @Override public void handle(ServerRequest req, ServerResponse res) { res.header(CONTENT_LENGTH); res.header(HeaderValues.CONTENT_TYPE_JSON); res.header(Main.SERVER); - res.send(serializeMsg(newMsg())); - } - - private static Message newMsg() { - return new Message("Hello, World!"); - } - } - - static class JsonKHandler implements Handler { - private final Header contentLength; - private final String message; - - JsonKHandler(int kilobytes) { - this.message = "a".repeat(1024 * kilobytes); - int length = serializeMsg(new Message(message)).length; - this.contentLength = HeaderValues.createCached(HeaderNames.CONTENT_LENGTH, - String.valueOf(length)); - } - - @Override - public void handle(ServerRequest req, ServerResponse res) { - res.header(contentLength); - res.header(HeaderValues.CONTENT_TYPE_JSON); - res.header(Main.SERVER); - res.send(serializeMsg(newMsg())); - } - - private Message newMsg() { - return new Message(message); + res.send(serialize(new Message(MESSAGE))); } } 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 204c9ad5ad1..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,42 +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.JsonArray; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - public interface DbRepository { - JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); - - default World getWorld() { - return getWorld(randomWorldNumber()); - } - World getWorld(int id); - default JsonObject getWorldAsJson(int id) { - return getWorld().toJson(); - } - List getWorlds(int count); - default JsonArray getWorldsAsJson(int count) { - JsonArrayBuilder result = JSON.createArrayBuilder(); - for (World world : getWorlds(count)) { - result.add(world.toJson()); - } - return result.build(); - } - - 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 686559b2fd9..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 @@ -7,6 +7,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.logging.Logger; import com.zaxxer.hikari.HikariConfig; @@ -22,20 +24,31 @@ public class HikariJdbcRepository implements DbRepository { private final HikariConfig hikariConfig; public HikariJdbcRepository(Config config) { + // hikari connection configuration String url = "jdbc:postgresql://" + config.get("host").asString().orElse("tfb-database") + ":" + config.get("port").asString().orElse("5432") + "/" + config.get("db").asString().orElse("hello_world"); - hikariConfig = new HikariConfig(); hikariConfig.setJdbcUrl(url); hikariConfig.setUsername(config.get("username").asString().orElse("benchmarkdbuser")); hikariConfig.setPassword(config.get("password").asString().orElse("benchmarkdbpass")); - hikariConfig.addDataSourceProperty("cachePrepStmts", "true"); + // hikari additional configuration int poolSize = config.get("sql-pool-size").asInt().orElse(64); - hikariConfig.addDataSourceProperty("maximumPoolSize", poolSize); - LOGGER.info("Db pool size is set to " + poolSize); + hikariConfig.setMaximumPoolSize(poolSize); + LOGGER.info("Hikari pool size is set to " + poolSize); + ThreadFactory vtThreadFactory = Thread.ofVirtual().factory(); + hikariConfig.setThreadFactory(vtThreadFactory); + hikariConfig.setScheduledExecutor(Executors.newScheduledThreadPool(poolSize, vtThreadFactory)); + LOGGER.info("Set thread factory to VTs"); + + // data source properties + hikariConfig.addDataSourceProperty("cachePrepStmts","true"); + hikariConfig.addDataSourceProperty("prepStmtCacheSize","250"); + hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit","2048"); + hikariConfig.addDataSourceProperty("ssl", "false"); + hikariConfig.addDataSourceProperty("tcpKeepAlive", "true"); } private Connection getConnection() throws SQLException { @@ -67,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 7775a177537..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,88 +2,70 @@ 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.common.reactive.Multi; -import io.helidon.common.reactive.Single; import io.helidon.config.Config; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.pgclient.PgConnectOptions; import io.vertx.pgclient.PgPool; import io.vertx.sqlclient.PoolOptions; +import io.vertx.sqlclient.PreparedQuery; import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; import io.vertx.sqlclient.SqlClient; import io.vertx.sqlclient.Tuple; -import jakarta.json.JsonArray; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonObject; import static io.helidon.benchmark.nima.models.DbRepository.randomWorldNumber; 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>[] 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); - } - @Override - public JsonObject getWorldAsJson(int id) { - return getWorld(id, queryPool).map(World::toJson).await(); - } + getWorldQuery = queryPool.preparedQuery("SELECT id, randomnumber FROM world WHERE id = $1"); + getFortuneQuery = queryPool.preparedQuery("SELECT id, message FROM fortune"); - @Override - public World getWorld(int id) { - try { - return getWorld(id, queryPool).toCompletableFuture().get(); - } catch (Exception e) { - throw new RuntimeException(e); + updateWorldSingleQuery = new PreparedQuery[UPDATE_QUERIES]; + for (int i = 0; i < UPDATE_QUERIES; i++) { + updateWorldSingleQuery[i] = queryPool.preparedQuery(singleUpdate(i + 1)); } } @Override - public JsonArray getWorldsAsJson(int count) { + public World getWorld(int id) { try { - return Multi.range(0, count) - .flatMap(i -> getWorld(randomWorldNumber(), queryPool)) - .map(World::toJson) - .reduce(JSON::createArrayBuilder, JsonArrayBuilder::add) - .map(JsonArrayBuilder::build) - .await(); + return getWorldQuery.execute(Tuple.of(id)) + .map(rows -> { + Row r = rows.iterator().next(); + return new World(r.getInteger(0), r.getInteger(1)); + }).toCompletionStage().toCompletableFuture().get(); } catch (Exception e) { throw new RuntimeException(e); } @@ -92,72 +74,25 @@ public JsonArray getWorldsAsJson(int count) { @Override public List getWorlds(int count) { try { - List result = new ArrayList<>(count); + List> futures = new ArrayList<>(); for (int i = 0; i < count; i++) { - World world = queryPool.preparedQuery("SELECT id, randomnumber FROM world WHERE id = $1") - .execute(Tuple.of(randomWorldNumber())) - .map(rows -> { - Row r = rows.iterator().next(); - return new World(r.getInteger(0), r.getInteger(1)); - }).toCompletionStage().toCompletableFuture().get(); - result.add(world); + futures.add(getWorldQuery.execute(Tuple.of(randomWorldNumber())) + .map(rows -> { + Row r = rows.iterator().next(); + return new World(r.getInteger(0), r.getInteger(1)); + })); } - return result; + return Future.all(futures).toCompletionStage().toCompletableFuture().get().list(); } catch (Exception e) { throw new RuntimeException(e); } } - @Override - public World updateWorld(World world) { - return Single.create(queryPool.preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2") - .execute(Tuple.of(world.id, world.id)) - .toCompletionStage() - .thenApply(rows -> world)).await(); - } - @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); } @@ -165,38 +100,50 @@ private List updateWorldsRetry(List worlds, int from, int retries) @Override public List getFortunes() { - return Single.create(queryPool.preparedQuery("SELECT id, message FROM fortune") - .execute() - .map(rows -> { - List fortunes = new ArrayList<>(rows.size() + 1); - for (Row r : rows) { - fortunes.add(new Fortune(r.getInteger(0), r.getString(1))); - } - return fortunes; - }).toCompletionStage()).await(); - } - - private static Single getWorld(int id, SqlClient pool) { - return Single.create(pool.preparedQuery("SELECT id, randomnumber FROM world WHERE id = $1") - .execute(Tuple.of(id)) - .map(rows -> { - Row r = rows.iterator().next(); - return new World(r.getInteger(0), r.getInteger(1)); - }).toCompletionStage()); - + try { + return getFortuneQuery.execute() + .map(rows -> { + List fortunes = new ArrayList<>(rows.size() + 1); + for (Row r : rows) { + fortunes.add(new Fortune(r.getInteger(0), r.getString(1))); + } + return fortunes; + }).toCompletionStage().toCompletableFuture().get(); + } catch (Exception e) { + throw new RuntimeException(e); + } } - 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 pool.preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2") - .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 ee8eb9194cd..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 { - private static final String ID_KEY = "id"; - private static final String ID_RANDOM_NUMBER = "randomNumber"; - private 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/java/io/helidon/benchmark/nima/services/DbService.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java index 46c244d96f5..e3bd1fe39fc 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java @@ -1,27 +1,23 @@ package io.helidon.benchmark.nima.services; -import java.util.Collections; import java.util.List; import io.helidon.benchmark.nima.models.DbRepository; import io.helidon.benchmark.nima.models.World; import io.helidon.common.parameters.Parameters; +import io.helidon.http.HeaderValues; import io.helidon.webserver.http.HttpRules; import io.helidon.webserver.http.HttpService; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; - -import jakarta.json.Json; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; +import io.helidon.common.mapper.OptionalValue; import static io.helidon.benchmark.nima.Main.SERVER; import static io.helidon.benchmark.nima.models.DbRepository.randomWorldNumber; +import static io.helidon.benchmark.nima.JsonSerializer.serialize; public class DbService implements HttpService { - private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); private final DbRepository repository; @@ -38,36 +34,33 @@ public void routing(HttpRules httpRules) { private void db(ServerRequest req, ServerResponse res) { res.header(SERVER); - res.send(repository.getWorldAsJson(randomWorldNumber())); + res.header(HeaderValues.CONTENT_TYPE_JSON); + res.send(serialize(repository.getWorld(randomWorldNumber()))); } private void queries(ServerRequest req, ServerResponse res) { res.header(SERVER); + res.header(HeaderValues.CONTENT_TYPE_JSON); int count = parseQueryCount(req.query()); - res.send(repository.getWorldsAsJson(count)); + res.send(serialize(repository.getWorlds(count))); } private void updates(ServerRequest req, ServerResponse res) { res.header(SERVER); + res.header(HeaderValues.CONTENT_TYPE_JSON); int count = parseQueryCount(req.query()); List worlds = repository.updateWorlds(count); - JsonArrayBuilder arrayBuilder = JSON.createArrayBuilder(); - for (World world : worlds) { - JsonObject json = world.toJson(); - arrayBuilder.add(json); - } - res.send(arrayBuilder.build()); + res.send(serialize(worlds)); } private int parseQueryCount(Parameters parameters) { - List values = parameters.all("queries"); - if (values.isEmpty()) { + OptionalValue value = parameters.first("queries"); + if (value.isEmpty()) { return 1; } - String first = values.get(0); int parsedValue; try { - parsedValue = Integer.parseInt(first, 10); + parsedValue = Integer.parseInt(value.get(), 10); } catch (NumberFormatException e) { return 1; } diff --git a/frameworks/Java/helidon/nima/src/main/resources/application.yaml b/frameworks/Java/helidon/nima/src/main/resources/application.yaml index ff4aa100b67..d2d8e8943b4 100644 --- a/frameworks/Java/helidon/nima/src/main/resources/application.yaml +++ b/frameworks/Java/helidon/nima/src/main/resources/application.yaml @@ -19,6 +19,7 @@ server: port: 8080 backlog: 8192 write-queue-length: 8192 + smart-async-writes: true connection-options: read-timeout: PT0S connect-timeout: PT0S @@ -38,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/pom.xml b/frameworks/Java/hserver/pom.xml index 4e304e8caf9..b841cbd60ab 100644 --- a/frameworks/Java/hserver/pom.xml +++ b/frameworks/Java/hserver/pom.xml @@ -11,7 +11,7 @@ hserver-parent cn.hserver - 3.4.M2 + 3.5.M2 UTF-8 diff --git a/frameworks/Java/hserver/src/main/java/com/test/hserver/StartApp.java b/frameworks/Java/hserver/src/main/java/com/test/hserver/StartApp.java index 87c5642d0b8..b4e215081da 100644 --- a/frameworks/Java/hserver/src/main/java/com/test/hserver/StartApp.java +++ b/frameworks/Java/hserver/src/main/java/com/test/hserver/StartApp.java @@ -13,8 +13,6 @@ public class StartApp { public static void main(String[] args) { - ConstConfig.bossPool = Runtime.getRuntime().availableProcessors()/2; - ConstConfig.workerPool = Runtime.getRuntime().availableProcessors(); HServerApplication.run(StartApp.class, 8888, args); } } diff --git a/frameworks/Java/httpserver-robaho/README.md b/frameworks/Java/httpserver-robaho/README.md new file mode 100755 index 00000000000..9317e30ce2d --- /dev/null +++ b/frameworks/Java/httpserver-robaho/README.md @@ -0,0 +1,32 @@ +# httpserver Benchmarking Test + +This is an alternative version of the [httpserver benchmarking test suite](../httpserver) + +Package [robaho.net.httpserver](https://github.com/robaho/httpserver) provides an implementation of `com.sun.net.httpserver` designed for virtual threads, thus requiring JDK21+. + +It can be used with platform threads using a `cached thread pool` which configures a thread per task, which is more efficient for a small number of clients (embedded systems). + +### Test Type Implementation Source Code + +* [JSON](src/main/java/benchmarks/Server.java) +* [Plaintext](src/main/java/benchmarks/Server.java) +* [Fortunes](src/main/java/benchmarks/Server.java) + +## Important Libraries +The tests were run with: +* [Jackson](https://github.com/FasterXML/jackson) +* [HikariCP](https://github.com/brettwooldridge/HikariCP) +* [HTTL](https://httl.github.io/en/) + +## Test URLs +### JSON + +http://localhost:8080/json + +### Plaintext + +http://localhost:8080/plaintext + +### Fortunes + +http://localhost:8080/fortunes diff --git a/frameworks/Java/httpserver-robaho/benchmark_config.json b/frameworks/Java/httpserver-robaho/benchmark_config.json new file mode 100755 index 00000000000..e3a66134723 --- /dev/null +++ b/frameworks/Java/httpserver-robaho/benchmark_config.json @@ -0,0 +1,44 @@ +{ + "framework": "httpserver-robaho", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "None", + "framework": "None", + "language": "Java", + "flavor": "None", + "orm": "Raw", + "platform": "httpserver", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "httpserver-robaho", + "notes": "", + "versus": "" + }, + "postgres": { + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "Postgres", + "framework": "None", + "language": "Java", + "flavor": "None", + "orm": "Raw", + "platform": "httpserver", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "httpserver-robaho-postgres", + "notes": "", + "versus": "" + } + } + ] +} diff --git a/frameworks/Java/httpserver-robaho/config.toml b/frameworks/Java/httpserver-robaho/config.toml new file mode 100644 index 00000000000..e69305d162c --- /dev/null +++ b/frameworks/Java/httpserver-robaho/config.toml @@ -0,0 +1,27 @@ +[framework] +name = "httpserver-robaho" + +[main] +urls.plaintext = "/plaintext" +urls.json = "/json" +approach = "Realistic" +classification = "Platform" +database = "None" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "httpserver" +webserver = "None" +versus = "" + +[postgres] +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Platform" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "httpserver" +webserver = "None" +versus = "" diff --git a/frameworks/Java/httpserver-robaho/httpserver-robaho-postgres.dockerfile b/frameworks/Java/httpserver-robaho/httpserver-robaho-postgres.dockerfile new file mode 100644 index 00000000000..858df742cc8 --- /dev/null +++ b/frameworks/Java/httpserver-robaho/httpserver-robaho-postgres.dockerfile @@ -0,0 +1,13 @@ +FROM jelastic/maven:3.9.5-openjdk-21 as maven +WORKDIR /httpserver-robaho +COPY pom.xml pom.xml +COPY src src +RUN mvn compile assembly:single -q + +FROM openjdk:21-jdk-slim +WORKDIR /httpserver-robaho +COPY --from=maven /httpserver-robaho/target/httpserver-robaho-1.0-jar-with-dependencies.jar app.jar + +EXPOSE 8080 + +CMD ["java", "-server", "-jar", "app.jar", "postgres"] diff --git a/frameworks/Java/httpserver-robaho/httpserver-robaho.dockerfile b/frameworks/Java/httpserver-robaho/httpserver-robaho.dockerfile new file mode 100644 index 00000000000..d02bada7709 --- /dev/null +++ b/frameworks/Java/httpserver-robaho/httpserver-robaho.dockerfile @@ -0,0 +1,13 @@ +FROM jelastic/maven:3.9.5-openjdk-21 as maven +WORKDIR /httpserver-robaho +COPY pom.xml pom.xml +COPY src src +RUN mvn compile assembly:single -q + +FROM openjdk:21-jdk-slim +WORKDIR /httpserver-robaho +COPY --from=maven /httpserver-robaho/target/httpserver-robaho-1.0-jar-with-dependencies.jar app.jar + +EXPOSE 8080 + +CMD ["java", "-server", "-jar", "app.jar"] diff --git a/frameworks/Java/httpserver-robaho/pom.xml b/frameworks/Java/httpserver-robaho/pom.xml new file mode 100644 index 00000000000..ce9dc2cd27e --- /dev/null +++ b/frameworks/Java/httpserver-robaho/pom.xml @@ -0,0 +1,83 @@ + + 4.0.0 + com.techempower + httpserver-robaho + 1.0 + jar + + UTF-8 + 21 + 21 + 21 + + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.postgresql + postgresql + 42.7.2 + + + com.zaxxer + HikariCP + 3.3.1 + + + com.github.httl + httl + 1.0.11 + + + org.slf4j + slf4j-simple + 1.7.25 + + + io.github.robaho + httpserver + 1.0.6 + compile + + + + + + true + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + false + + + + maven-assembly-plugin + 3.1.0 + + + + benchmarks.Server + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + diff --git a/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Fortune.java b/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Fortune.java new file mode 100644 index 00000000000..2b420fb4764 --- /dev/null +++ b/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Fortune.java @@ -0,0 +1,25 @@ +package benchmarks; + +public class Fortune implements Comparable { + + private final int id; + private final String message; + + public Fortune(int id, String message) { + this.id = id; + this.message = message; + } + + public int getId() { + return id; + } + + public String getMessage() { + return message; + } + + @Override + public int compareTo(Fortune other) { + return message.compareTo(other.message); + } +} diff --git a/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Message.java b/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Message.java new file mode 100755 index 00000000000..4c1ad6dc031 --- /dev/null +++ b/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Message.java @@ -0,0 +1,26 @@ +package benchmarks; + +import java.io.IOException; +import java.io.Writer; +import java.util.Map; + +import org.json.simple.JSONStreamAware; +import org.json.simple.JSONValue; + +public class Message implements JSONStreamAware { + + private final String message; + + public Message(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + @Override + public void writeJSONString(Writer out) throws IOException { + JSONValue.writeJSONString(Map.of("message",message), out); + } +} diff --git a/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Server.java b/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Server.java new file mode 100755 index 00000000000..338200488d0 --- /dev/null +++ b/frameworks/Java/httpserver-robaho/src/main/java/benchmarks/Server.java @@ -0,0 +1,139 @@ +package benchmarks; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.InetSocketAddress; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executors; + +import javax.sql.DataSource; + +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import httl.Engine; +import httl.Template; + +public class Server { + + private static final String HELLO_TEXT = "Hello, World!"; + private static final byte[] HELLO_BYTES = HELLO_TEXT.getBytes(); + private static final int HELLO_LENGTH = HELLO_BYTES.length; + private static final String SERVER_NAME = "httpserver-robaho"; + + private static List queryFortunes(DataSource ds) throws SQLException { + List fortunes = new ArrayList<>(); + try (Connection conn = ds.getConnection(); + PreparedStatement statement = conn.prepareStatement("SELECT id, message FROM fortune"); + ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) + fortunes.add(new Fortune(resultSet.getInt(1), resultSet.getString(2))); + } + return fortunes; + } + + private static DataSource createPostgresDataSource() throws ClassNotFoundException { + Class.forName("org.postgresql.Driver"); + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:postgresql://tfb-database:5432/hello_world"); + config.setUsername("benchmarkdbuser"); + config.setPassword("benchmarkdbpass"); + config.setMaximumPoolSize(512); + return new HikariDataSource(config); + } + + private static Template loadTemplate(String filename) throws IOException, ParseException { + Properties props = new Properties(); + props.put("import.packages", "java.util," + Fortune.class.getPackage().getName()); + props.put("input.encoding", "UTF-8"); + props.put("output.encoding", "UTF-8"); + props.put("precompiled", "false"); + Engine engine = Engine.getEngine(props); + return engine.getTemplate(filename); + } + + private static HttpHandler createPlaintextHandler() { + return t -> { + t.getResponseHeaders().add("Content-Type", "text/plain"); + t.getResponseHeaders().add("Server", SERVER_NAME); + t.sendResponseHeaders(200, HELLO_LENGTH); + t.getResponseBody().write(HELLO_BYTES); + t.getResponseBody().close(); + }; + } + + private static HttpHandler createJSONHandler() { + return t -> { + Message m = new Message(HELLO_TEXT); + t.getResponseHeaders().add("Content-Type", "application/json"); + t.getResponseHeaders().add("Server", SERVER_NAME); + var bos = new ByteArrayOutputStream(); + OutputStreamWriter w = new OutputStreamWriter(bos); + m.writeJSONString(w); + w.flush(); + t.sendResponseHeaders(200, bos.size()); + bos.writeTo(t.getResponseBody()); + t.getResponseBody().close(); + }; + } + + private static HttpHandler createFortunesHandler(DataSource ds) throws IOException, ParseException { + Template template = loadTemplate("/fortunes.template.httl"); + return t -> { + try { + // query db + List fortunes = queryFortunes(ds); + fortunes.add(new Fortune(0, "Additional fortune added at request time.")); + Collections.sort(fortunes); + // render template + Map context = new HashMap<>(1); + context.put("fortunes", fortunes); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + template.render(context, out); + byte[] bytes = out.toByteArray(); + // send response + t.getResponseHeaders().add("Content-Type", "text/html; charset=utf-8"); + t.getResponseHeaders().add("Server", SERVER_NAME); + t.sendResponseHeaders(200, bytes.length); + t.getResponseBody().write(bytes); + t.getResponseBody().close(); + } catch (SQLException | ParseException e) { + throw new IOException(e); + } + }; + } + + public static void main(String[] args) throws Exception { + // parse arguments + String settings = args.length > 0 ? args[0] : ""; + int port = args.length > 1 ? Integer.parseInt(args[1]) : 8080; + if (settings.contains("debug")) + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG"); + // create server + HttpServer server = HttpServer.create(new InetSocketAddress(port), 1024 * 8); + server.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); + // server.setExecutor(Executors.newCachedThreadPool()); + // add context handlers + server.createContext("/plaintext", createPlaintextHandler()); + server.createContext("/json", createJSONHandler()); + if (settings.contains("postgres")) { + DataSource ds = createPostgresDataSource(); + server.createContext("/fortunes", createFortunesHandler(ds)); + } + // start server + server.start(); + } +} diff --git a/frameworks/Java/httpserver-robaho/src/main/resources/fortunes.template.httl b/frameworks/Java/httpserver-robaho/src/main/resources/fortunes.template.httl new file mode 100644 index 00000000000..4d87b47658a --- /dev/null +++ b/frameworks/Java/httpserver-robaho/src/main/resources/fortunes.template.httl @@ -0,0 +1,13 @@ + + + +Fortunes + + + + + + +
idmessage
${fortune.id}${fortune.message}
+ + diff --git a/frameworks/Java/jetty/pom.xml b/frameworks/Java/jetty/pom.xml index db5ba4a6b7b..6156c5603f8 100644 --- a/frameworks/Java/jetty/pom.xml +++ b/frameworks/Java/jetty/pom.xml @@ -11,7 +11,7 @@ UTF-8 11 11 - 10.0.14 + 10.0.24 hello.handler.HelloWebServer
diff --git a/frameworks/Java/jooby/jooby-jetty.dockerfile b/frameworks/Java/jooby/jooby-jetty.dockerfile index bd3786886ca..c5d636ada2d 100644 --- a/frameworks/Java/jooby/jooby-jetty.dockerfile +++ b/frameworks/Java/jooby/jooby-jetty.dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.9.6-eclipse-temurin-21-jammy +FROM maven:3.9.9-eclipse-temurin-22-alpine WORKDIR /jooby COPY pom.xml pom.xml COPY src src diff --git a/frameworks/Java/jooby/jooby-mvc.dockerfile b/frameworks/Java/jooby/jooby-mvc.dockerfile index feaeb23bc8e..b2a4db4712b 100644 --- a/frameworks/Java/jooby/jooby-mvc.dockerfile +++ b/frameworks/Java/jooby/jooby-mvc.dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.9.6-eclipse-temurin-21-jammy +FROM maven:3.9.9-eclipse-temurin-22-alpine WORKDIR /jooby COPY pom.xml pom.xml COPY src src diff --git a/frameworks/Java/jooby/jooby-netty.dockerfile b/frameworks/Java/jooby/jooby-netty.dockerfile index dd6afe017c7..1c3efc2585e 100644 --- a/frameworks/Java/jooby/jooby-netty.dockerfile +++ b/frameworks/Java/jooby/jooby-netty.dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.9.6-eclipse-temurin-21-jammy +FROM maven:3.9.9-eclipse-temurin-22-alpine WORKDIR /jooby COPY pom.xml pom.xml COPY src src diff --git a/frameworks/Java/jooby/jooby-pgclient.dockerfile b/frameworks/Java/jooby/jooby-pgclient.dockerfile index e3c58df435c..044cdd5c5de 100644 --- a/frameworks/Java/jooby/jooby-pgclient.dockerfile +++ b/frameworks/Java/jooby/jooby-pgclient.dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.9.6-eclipse-temurin-21-jammy +FROM maven:3.9.9-eclipse-temurin-22-alpine WORKDIR /jooby COPY pom.xml pom.xml COPY src src diff --git a/frameworks/Java/jooby/jooby.dockerfile b/frameworks/Java/jooby/jooby.dockerfile index 98d1b029d36..a5d4570a60d 100644 --- a/frameworks/Java/jooby/jooby.dockerfile +++ b/frameworks/Java/jooby/jooby.dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.9.6-eclipse-temurin-21-jammy +FROM maven:3.9.9-eclipse-temurin-22-alpine WORKDIR /jooby COPY pom.xml pom.xml COPY src src diff --git a/frameworks/Java/jooby/pom.xml b/frameworks/Java/jooby/pom.xml index 338f8ca065d..3b6edfc79ec 100644 --- a/frameworks/Java/jooby/pom.xml +++ b/frameworks/Java/jooby/pom.xml @@ -11,13 +11,13 @@ jooby - 3.1.2 - 4.1.110.Final + 3.4.0 + 4.1.113.Final 2.0.2 - 42.7.3 + 42.7.4 UTF-8 - 21 - 21 + 22 + 22 com.techempower.App @@ -72,7 +72,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.5.0 + 3.6.0 add-source @@ -110,7 +110,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.13.0 @@ -130,7 +130,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.6.0 uber-jar diff --git a/frameworks/Java/jooby/src/main/java/com/techempower/App.java b/frameworks/Java/jooby/src/main/java/com/techempower/App.java index f7cf3373b4a..7a75f05d60b 100644 --- a/frameworks/Java/jooby/src/main/java/com/techempower/App.java +++ b/frameworks/Java/jooby/src/main/java/com/techempower/App.java @@ -28,13 +28,8 @@ public class App extends Jooby { private static final byte[] MESSAGE_BYTES = MESSAGE.getBytes(StandardCharsets.US_ASCII); - private static final ByteBuffer MESSAGE_BUFFER = (ByteBuffer) ByteBuffer - .allocateDirect(MESSAGE_BYTES.length) - .put(MESSAGE_BYTES) - .flip(); - { - + var bufferFactory = getBufferFactory(); /** Database: */ install(new HikariModule()); DataSource ds = require(DataSource.class); @@ -43,7 +38,7 @@ public class App extends Jooby { install(new RockerModule()); get("/plaintext", ctx -> - ctx.send(MESSAGE_BUFFER.duplicate()) + ctx.send(bufferFactory.wrap(MESSAGE_BYTES)) ); get("/json", ctx -> ctx diff --git a/frameworks/Java/jooby/src/main/java/com/techempower/MvcApp.java b/frameworks/Java/jooby/src/main/java/com/techempower/MvcApp.java index 68d09e0a472..ad40629a635 100644 --- a/frameworks/Java/jooby/src/main/java/com/techempower/MvcApp.java +++ b/frameworks/Java/jooby/src/main/java/com/techempower/MvcApp.java @@ -17,7 +17,7 @@ public static void main(String[] args) { /** Template engine: */ app.install(new RockerModule()); - app.mvc(new Resource(app.require(DataSource.class))); + app.mvc(new Resource_(app.require(DataSource.class))); }); } } diff --git a/frameworks/Java/jooby/src/main/java/com/techempower/PgClient.java b/frameworks/Java/jooby/src/main/java/com/techempower/PgClient.java index 4847ddab1de..dd5e9197f01 100644 --- a/frameworks/Java/jooby/src/main/java/com/techempower/PgClient.java +++ b/frameworks/Java/jooby/src/main/java/com/techempower/PgClient.java @@ -1,5 +1,7 @@ package com.techempower; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.function.BiConsumer; @@ -32,10 +34,13 @@ public class PgClient { private static final String SELECT_FORTUNE = "SELECT id, message from FORTUNE"; private static class DbConnection { + private SqlClientInternal queries; private PreparedQuery> SELECT_WORLD_QUERY; private PreparedQuery> SELECT_FORTUNE_QUERY; private PreparedQuery> UPDATE_WORLD_QUERY; - private SqlClientInternal connection; + private SqlClientInternal updates; + @SuppressWarnings("unchecked") + private PreparedQuery>[] AGGREGATED_UPDATE_WORLD_QUERY = new PreparedQuery[128]; } private static class DbConnectionFactory extends ThreadLocal { @@ -64,20 +69,32 @@ private Handler> onSuccess(Handler handler) { .setWorkerPoolSize(1) .setInternalBlockingPoolSize(1) ); - var future = PgConnection.connect(vertx, options) + var client1 = PgConnection.connect(vertx, options) .flatMap(conn -> { - result.connection = (SqlClientInternal) conn; + result.queries = (SqlClientInternal) conn; Future f1 = conn.prepare(SELECT_WORLD) .andThen(onSuccess(ps -> result.SELECT_WORLD_QUERY = ps.query())); Future f2 = conn.prepare(SELECT_FORTUNE) .andThen(onSuccess(ps -> result.SELECT_FORTUNE_QUERY = ps.query())); - Future f3 = conn.prepare(UPDATE_WORLD) - .andThen(onSuccess(ps -> result.UPDATE_WORLD_QUERY = ps.query())); - return Future.join(f1, f2, f3); - }) - .toCompletionStage() - .toCompletableFuture() - .get(); + return Future.join(f1, f2); + }); + + var client2 = PgConnection.connect(vertx, options) + .flatMap(conn -> { + result.updates = (SqlClientInternal) conn; + List> list = new ArrayList<>(); + Future f1 = conn.prepare(UPDATE_WORLD) + .andThen(onSuccess(ps -> result.UPDATE_WORLD_QUERY = ps.query())); + list.add(f1); + for (int i = 0; i < result.AGGREGATED_UPDATE_WORLD_QUERY.length; i++) { + int idx = i; + list.add(conn + .prepare(buildAggregatedUpdateQuery(1 + idx)) + .andThen(onSuccess(ps -> result.AGGREGATED_UPDATE_WORLD_QUERY[idx] = ps.query()))); + } + return Future.join(list); + }); + var future = Future.join(client1, client2).toCompletionStage().toCompletableFuture().get(); Throwable cause = future.cause(); if (cause != null) { @@ -91,6 +108,18 @@ private Handler> onSuccess(Handler handler) { throw SneakyThrows.propagate(ex.getCause()); } } + + private static String buildAggregatedUpdateQuery(int len) { + StringBuilder sb = new StringBuilder(); + sb.append("UPDATE world SET randomNumber = update_data.randomNumber FROM (VALUES"); + char sep = ' '; + for (int i = 1;i <= len;i++) { + sb.append(sep).append("($").append(2 * i - 1).append("::int,$").append(2 * i).append("::int)"); + sep = ','; + } + sb.append(") AS update_data (id, randomNumber) WHERE world.id = update_data.id"); + return sb.toString(); + } } private final ThreadLocal sqlClient; @@ -104,7 +133,7 @@ public void selectWorld(Tuple row, Handler>> handler) { } public void selectWorlds(int queries, Handler>> handler) { - this.sqlClient.get().connection.group(c -> { + this.sqlClient.get().queries.group(c -> { for (int i = 0; i < queries; i++) { c.preparedQuery(SELECT_WORLD).execute(Tuple.of(Util.randomWorld()), handler); } @@ -117,7 +146,7 @@ public void fortunes(Handler>> handler) { public void selectWorldForUpdate(int queries, BiConsumer>> consumer) { - this.sqlClient.get().connection.group(c -> { + this.sqlClient.get().queries.group(c -> { PreparedQuery> statement = c.preparedQuery(SELECT_WORLD); for (int i = 0; i < queries; i++) { consumer.accept(i, statement); @@ -125,8 +154,26 @@ public void selectWorldForUpdate(int queries, }); } - public void updateWorld(List batch, Handler>> handler) { - this.sqlClient.get().UPDATE_WORLD_QUERY.executeBatch(batch, handler); + public void updateWorld(World[] worlds, Handler>> handler) { + Arrays.sort(worlds); + int len = worlds.length; + var connection = this.sqlClient.get(); + if (0 < len && len <= connection.AGGREGATED_UPDATE_WORLD_QUERY.length) { + List arguments = new ArrayList<>(); + for (World world : worlds) { + arguments.add(world.getId()); + arguments.add(world.getRandomNumber()); + } + Tuple tuple = Tuple.tuple(arguments); + PreparedQuery> query = connection.AGGREGATED_UPDATE_WORLD_QUERY[len - 1]; + query.execute(tuple, handler); + } else { + List batch = new ArrayList<>(); + for (World world : worlds) { + batch.add(Tuple.of(world.getRandomNumber(), world.getId())); + } + connection.UPDATE_WORLD_QUERY.executeBatch(batch, handler); + } } private PgConnectOptions pgPoolOptions(Config config) { diff --git a/frameworks/Java/jooby/src/main/java/com/techempower/ReactivePg.java b/frameworks/Java/jooby/src/main/java/com/techempower/ReactivePg.java index 6a674c5e8df..c0eef6fb05a 100644 --- a/frameworks/Java/jooby/src/main/java/com/techempower/ReactivePg.java +++ b/frameworks/Java/jooby/src/main/java/com/techempower/ReactivePg.java @@ -7,10 +7,8 @@ import java.util.*; import com.fizzed.rocker.RockerOutputFactory; -import io.jooby.Context; -import io.jooby.Jooby; -import io.jooby.MediaType; -import io.jooby.ServerOptions; +import com.techempower.rocker.BufferRockerOutput; +import io.jooby.*; import io.jooby.rocker.DataBufferOutput; import io.jooby.rocker.RockerModule; import io.vertx.sqlclient.Row; @@ -18,7 +16,6 @@ import io.vertx.sqlclient.Tuple; public class ReactivePg extends Jooby { - { /** Reduce the number of resources due we do reactive processing. */ setServerOptions( @@ -84,14 +81,7 @@ public class ReactivePg extends Jooby { selectCallback.result().iterator().next().getInteger(0), randomWorld()); if (index == queries - 1) { - // Sort results... avoid dead locks - Arrays.sort(result); - List batch = new ArrayList<>(queries); - for (World world : result) { - batch.add(Tuple.of(world.getRandomNumber(), world.getId())); - } - - client.updateWorld(batch, updateCallback -> { + client.updateWorld(result, updateCallback -> { if (updateCallback.failed()) { sendError(ctx, updateCallback.cause()); } else { @@ -106,7 +96,7 @@ public class ReactivePg extends Jooby { }).setNonBlocking(true); /** Fortunes: */ - RockerOutputFactory factory = require(RockerOutputFactory.class); + var factory = BufferRockerOutput.factory(); get("/fortunes", ctx -> { client.fortunes(rsp -> { if (rsp.succeeded()) { diff --git a/frameworks/Java/jooby/src/main/java/com/techempower/rocker/BufferRockerOutput.java b/frameworks/Java/jooby/src/main/java/com/techempower/rocker/BufferRockerOutput.java new file mode 100644 index 00000000000..78abf2056e4 --- /dev/null +++ b/frameworks/Java/jooby/src/main/java/com/techempower/rocker/BufferRockerOutput.java @@ -0,0 +1,65 @@ +package com.techempower.rocker; + +import com.fizzed.rocker.ContentType; +import com.fizzed.rocker.RockerOutput; +import com.fizzed.rocker.RockerOutputFactory; + +import java.io.IOException; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +public class BufferRockerOutput implements RockerOutput { + private final ByteBuffer buffer; + + public BufferRockerOutput(ByteBuffer buffer) { + this.buffer = buffer; + } + + @Override + public ContentType getContentType() { + return ContentType.RAW; + } + + @Override + public Charset getCharset() { + return StandardCharsets.UTF_8; + } + + @Override + public BufferRockerOutput w(String string) throws IOException { + buffer.put(string.getBytes(getCharset())); + return this; + } + + @Override + public BufferRockerOutput w(byte[] bytes) throws IOException { + buffer.put(bytes); + return this; + } + + @Override + public int getByteLength() { + return buffer.remaining(); + } + + public ByteBuffer toBuffer() { + return buffer.flip(); + } + + public static RockerOutputFactory factory() { + var cache = new ThreadLocal() { + @Override + protected BufferRockerOutput initialValue() { + return new BufferRockerOutput(ByteBuffer.allocateDirect(2048)); + } + }; + return (contentType, charsetName) -> cache.get().reset(); + } + + private BufferRockerOutput reset() { + buffer.clear(); + return this; + } +} diff --git a/frameworks/Java/light-java/pom.xml b/frameworks/Java/light-java/pom.xml index 0d76eb4e631..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.12.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 7f10f90ed59..386638337fe 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.3.7" + implementation "io.micronaut.gradle:micronaut-gradle-plugin:4.4.2" 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/buildSrc/src/main/groovy/io.micronaut.benchmark.module.gradle b/frameworks/Java/micronaut/buildSrc/src/main/groovy/io.micronaut.benchmark.module.gradle index 43a854cf848..dcb6b3389fc 100644 --- a/frameworks/Java/micronaut/buildSrc/src/main/groovy/io.micronaut.benchmark.module.gradle +++ b/frameworks/Java/micronaut/buildSrc/src/main/groovy/io.micronaut.benchmark.module.gradle @@ -28,7 +28,7 @@ dependencies { } graalvmNative.binaries.all { - buildArgs.add("--initialize-at-build-time=views") + buildArgs.add("--initialize-at-build-time=gg.jte.generated.precompiled") } test { diff --git a/frameworks/Java/micronaut/common/build.gradle b/frameworks/Java/micronaut/common/build.gradle index c254fb8e743..8153fd5d8ea 100644 --- a/frameworks/Java/micronaut/common/build.gradle +++ b/frameworks/Java/micronaut/common/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id "io.micronaut.library" - id "nu.studer.rocker" version "3.0.4" + id "gg.jte.gradle" version "3.1.12" } group 'io.micronaut.benchmark' @@ -16,14 +16,10 @@ micronaut { testRuntime "junit5" } -rocker { - configurations { - main { - templateDir = file('src/main/resources') - outputDir = file('build/generated/rocker') - optimize = true - } - } +jte { + sourceDirectory = file("src/main/jte").toPath() + generate() + binaryStaticContent = true } dependencies { @@ -38,7 +34,8 @@ dependencies { transitive = false } - implementation("com.fizzed:rocker-runtime") + // Switch to BOM version after https://github.com/micronaut-projects/micronaut-views/issues/876 + implementation("gg.jte:jte-runtime:3.1.12") runtimeOnly("ch.qos.logback:logback-classic") runtimeOnly("org.yaml:snakeyaml") @@ -46,4 +43,11 @@ dependencies { test { useJUnitPlatform() +} + +// Gradle requires that generateJte is run before some tasks +tasks.configureEach { + if (name == "inspectRuntimeClasspath") { + mustRunAfter("generateJte") + } } \ No newline at end of file diff --git a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AbstractBenchmarkController.java b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AbstractBenchmarkController.java index 637649b448e..8f595d99931 100644 --- a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AbstractBenchmarkController.java +++ b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AbstractBenchmarkController.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.IntStream; @@ -12,6 +13,20 @@ public class AbstractBenchmarkController { protected final Integer[] boxed = IntStream.range(1, 10001).boxed().toArray(Integer[]::new); + private static final Comparator FORTUNES_COMPARATOR = new Comparator<>() { + @Override + public int compare(Fortune o1, Fortune o2) { + return o1.message().compareTo(o2.message()); + } + }; + + protected List prepareFortunes(List fortuneList) { + List all = new ArrayList<>(fortuneList.size() + 1); + all.add(new Fortune(0, "Additional fortune added at request time.")); + all.addAll(fortuneList); + all.sort(FORTUNES_COMPARATOR); + return all; + } protected List createFortunes() { List fortuneMessages = IntStream.range(0, 10).boxed().toList(); diff --git a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AsyncBenchmarkController.java b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AsyncBenchmarkController.java index 7e40c36a204..a16ce1933c3 100644 --- a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AsyncBenchmarkController.java +++ b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AsyncBenchmarkController.java @@ -5,11 +5,9 @@ import benchmark.repository.AsyncFortuneRepository; import benchmark.repository.AsyncWorldRepository; import io.micronaut.context.annotation.Requires; -import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.QueryValue; -import views.fortunes; import java.util.ArrayList; import java.util.Comparator; @@ -55,15 +53,8 @@ public CompletionStage> queries(@QueryValue String queries) { // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#fortunes @Get(value = "/fortunes", produces = "text/html;charset=utf-8") - public CompletionStage> fortune() { - return fortuneRepository.findAll().thenApply(fortuneList -> { - List all = new ArrayList<>(fortuneList.size() + 1); - all.add(new Fortune(0, "Additional fortune added at request time.")); - all.addAll(fortuneList); - all.sort(comparing(Fortune::message)); - String body = fortunes.template(all).render().toString(); - return HttpResponse.ok(body).contentType("text/html;charset=utf-8"); - }); + public CompletionStage> fortune() { + return fortuneRepository.findAll().thenApply(this::prepareFortunes); } // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates diff --git a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/BenchmarkController.java b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/BenchmarkController.java index c00eed41fb4..f38940257d3 100644 --- a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/BenchmarkController.java +++ b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/BenchmarkController.java @@ -5,19 +5,14 @@ import benchmark.repository.FortuneRepository; import benchmark.repository.WorldRepository; import io.micronaut.context.annotation.Requires; -import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.QueryValue; -import views.fortunes; import java.util.ArrayList; -import java.util.Collection; import java.util.Comparator; import java.util.List; -import static java.util.Comparator.comparing; - @Requires(beans = {WorldRepository.class, FortuneRepository.class}) @Controller public class BenchmarkController extends AbstractBenchmarkController { @@ -56,14 +51,8 @@ public List queries(@QueryValue String queries) { // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#fortunes @Get(value = "/fortunes", produces = "text/html;charset=utf-8") - public HttpResponse fortune() { - Collection all = fortuneRepository.findAll(); - List fortunesList = new ArrayList<>(all.size() + 1); - fortunesList.add(new Fortune(0, "Additional fortune added at request time.")); - fortunesList.addAll(all); - fortunesList.sort(comparing(Fortune::message)); - String body = fortunes.template(fortunesList).render().toString(); - return HttpResponse.ok(body).contentType("text/html;charset=utf-8"); + public List fortune() { + return prepareFortunes(fortuneRepository.findAll()); } // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates @@ -77,5 +66,5 @@ public List updates(@QueryValue String queries) { worldRepository.updateAll(worldList); return worldList; } - + } diff --git a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/FortunesBodyWriter.java b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/FortunesBodyWriter.java new file mode 100644 index 00000000000..a7bee37d493 --- /dev/null +++ b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/FortunesBodyWriter.java @@ -0,0 +1,54 @@ +package benchmark.controller; + +import benchmark.model.Fortune; +import gg.jte.TemplateOutput; +import gg.jte.generated.precompiled.JtefortunesGenerated; +import gg.jte.html.OwaspHtmlTemplateOutput; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.type.Argument; +import io.micronaut.core.type.MutableHeaders; +import io.micronaut.http.MediaType; +import io.micronaut.http.body.MessageBodyWriter; +import io.micronaut.http.codec.CodecException; +import io.micronaut.http.netty.NettyHttpHeaders; +import jakarta.inject.Singleton; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +@Singleton +public class FortunesBodyWriter implements MessageBodyWriter> { + + @Override + public void writeTo(@NonNull Argument> type, + @NonNull MediaType mediaType, + List values, + @NonNull MutableHeaders outgoingHeaders, + @NonNull OutputStream outputStream) throws CodecException { + outgoingHeaders.set(NettyHttpHeaders.CONTENT_TYPE, "text/html;charset=utf-8"); + TemplateOutput output = new TemplateOutput() { + + @Override + public void writeContent(String value) { + writeBinaryContent(value.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public void writeContent(String value, int beginIndex, int endIndex) { + writeBinaryContent(value.substring(beginIndex, endIndex).getBytes(StandardCharsets.UTF_8)); + } + + @Override + public void writeBinaryContent(byte[] value) { + try { + outputStream.write(value); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + JtefortunesGenerated.render(new OwaspHtmlTemplateOutput(output), null, values); + } +} diff --git a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/ReactiveBenchmarkController.java b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/ReactiveBenchmarkController.java index 8adbc4c4716..e85d236acee 100644 --- a/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/ReactiveBenchmarkController.java +++ b/frameworks/Java/micronaut/common/src/main/java/benchmark/controller/ReactiveBenchmarkController.java @@ -6,21 +6,17 @@ import benchmark.repository.ReactiveWorldRepository; import io.micronaut.context.annotation.Requires; import io.micronaut.core.async.annotation.SingleResult; -import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.QueryValue; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import views.fortunes; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import static java.util.Comparator.comparing; - @Requires(beans = {ReactiveWorldRepository.class, ReactiveFortuneRepository.class}) @Controller public class ReactiveBenchmarkController extends AbstractBenchmarkController { @@ -61,15 +57,8 @@ public Publisher> queries(@QueryValue String queries) { // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#fortunes @Get(value = "/fortunes", produces = "text/html;charset=utf-8") @SingleResult - public Mono> fortune() { - return Mono.from(fortuneRepository.findAll()).map(fortuneList -> { - List all = new ArrayList<>(fortuneList.size() + 1); - all.add(new Fortune(0, "Additional fortune added at request time.")); - all.addAll(fortuneList); - all.sort(comparing(Fortune::message)); - String body = fortunes.template(all).render().toString(); - return HttpResponse.ok(body).contentType("text/html;charset=utf-8"); - }); + public Mono> fortune() { + return Mono.from(fortuneRepository.findAll()).map(this::prepareFortunes); } // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates diff --git a/frameworks/Java/micronaut/common/src/main/java/benchmark/repository/FortuneRepository.java b/frameworks/Java/micronaut/common/src/main/java/benchmark/repository/FortuneRepository.java index 701f647e384..b1c8fe3989e 100644 --- a/frameworks/Java/micronaut/common/src/main/java/benchmark/repository/FortuneRepository.java +++ b/frameworks/Java/micronaut/common/src/main/java/benchmark/repository/FortuneRepository.java @@ -4,11 +4,12 @@ import org.reactivestreams.Publisher; import java.util.Collection; +import java.util.List; public interface FortuneRepository { void initDb(Collection fortunes); - Collection findAll(); + List findAll(); } diff --git a/frameworks/Java/micronaut/common/src/main/jte/fortunes.jte b/frameworks/Java/micronaut/common/src/main/jte/fortunes.jte new file mode 100644 index 00000000000..7ac61415c99 --- /dev/null +++ b/frameworks/Java/micronaut/common/src/main/jte/fortunes.jte @@ -0,0 +1,4 @@ +@param java.util.List fortunes +Fortunes@for(benchmark.model.Fortune fortune : fortunes) + @endfor +
idmessage
${fortune.id()}${fortune.message()}
\ No newline at end of file diff --git a/frameworks/Java/micronaut/common/src/main/resources/views/fortunes.rocker.html b/frameworks/Java/micronaut/common/src/main/resources/views/fortunes.rocker.html deleted file mode 100644 index c6b9d0124a6..00000000000 --- a/frameworks/Java/micronaut/common/src/main/resources/views/fortunes.rocker.html +++ /dev/null @@ -1,8 +0,0 @@ -@import java.util.* -@import benchmark.model.* -@args(List fortunes) -Fortunes - @for ((ForIterator i, Fortune fortune) : fortunes) { - - } -
idmessage
@fortune.id()@fortune.message()
\ No newline at end of file diff --git a/frameworks/Java/micronaut/gradle.properties b/frameworks/Java/micronaut/gradle.properties index ba63eab22c9..8ce6479ffe6 100644 --- a/frameworks/Java/micronaut/gradle.properties +++ b/frameworks/Java/micronaut/gradle.properties @@ -1 +1 @@ -micronautVersion=4.5.0 +micronautVersion=4.6.1 diff --git a/frameworks/Java/micronaut/gradle/wrapper/gradle-wrapper.properties b/frameworks/Java/micronaut/gradle/wrapper/gradle-wrapper.properties index b82aa23a4f0..9355b415575 100644 --- a/frameworks/Java/micronaut/gradle/wrapper/gradle-wrapper.properties +++ b/frameworks/Java/micronaut/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile index 63eb26907b0..8d7377b166d 100644 --- a/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile @@ -1,12 +1,11 @@ -FROM ghcr.io/graalvm/native-image-community:21-ol9 as build +FROM container-registry.oracle.com/graalvm/native-image:21 RUN microdnf install findutils # Gradle 8.7 requires xargs COPY . /home/gradle/src WORKDIR /home/gradle/src RUN ./gradlew micronaut-data-jdbc:nativeCompile -x test -x internalStartTestResourcesService --no-daemon -FROM cgr.dev/chainguard/wolfi-base:latest WORKDIR /micronaut -COPY --from=build /home/gradle/src/micronaut-data-jdbc/build/native/nativeCompile/micronaut-data-jdbc micronaut +RUN mv /home/gradle/src/micronaut-data-jdbc/build/native/nativeCompile/micronaut-data-jdbc micronaut EXPOSE 8080 ENV MICRONAUT_ENVIRONMENTS=benchmark diff --git a/frameworks/Java/micronaut/micronaut-data-jdbc.dockerfile b/frameworks/Java/micronaut/micronaut-data-jdbc.dockerfile index 60c935c6d25..f506d3e9fb8 100644 --- a/frameworks/Java/micronaut/micronaut-data-jdbc.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-jdbc.dockerfile @@ -3,7 +3,7 @@ COPY --chown=gradle:gradle . /home/gradle/src WORKDIR /home/gradle/src RUN gradle micronaut-data-jdbc:build -x test -x internalStartTestResourcesService --no-daemon -FROM openjdk:21 +FROM openjdk:23 WORKDIR /micronaut COPY --from=build /home/gradle/src/micronaut-data-jdbc/build/libs/micronaut-data-jdbc-all.jar micronaut.jar COPY run_benchmark.sh run_benchmark.sh diff --git a/frameworks/Java/micronaut/micronaut-data-jdbc/src/main/java/benchmark/JdbcWorldRepository.java b/frameworks/Java/micronaut/micronaut-data-jdbc/src/main/java/benchmark/JdbcWorldRepository.java index 3b08db89390..89e9a6be895 100644 --- a/frameworks/Java/micronaut/micronaut-data-jdbc/src/main/java/benchmark/JdbcWorldRepository.java +++ b/frameworks/Java/micronaut/micronaut-data-jdbc/src/main/java/benchmark/JdbcWorldRepository.java @@ -2,6 +2,7 @@ import benchmark.model.World; import benchmark.repository.WorldRepository; +import io.micronaut.data.connection.annotation.Connectable; import io.micronaut.data.jdbc.annotation.JdbcRepository; import io.micronaut.data.model.query.builder.sql.Dialect; import io.micronaut.data.repository.GenericRepository; @@ -20,6 +21,7 @@ default void initDb(Collection worlds) { void saveAll(Collection worlds); + @Connectable @Override default List findByIds(List ids) { return WorldRepository.super.findByIds(ids); diff --git a/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile index 85472860d78..f0b28e5bcf3 100644 --- a/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile @@ -1,12 +1,11 @@ -FROM ghcr.io/graalvm/native-image-community:21-ol9 as build +FROM container-registry.oracle.com/graalvm/native-image:21 RUN microdnf install findutils # Gradle 8.7 requires xargs COPY . /home/gradle/src WORKDIR /home/gradle/src RUN ./gradlew micronaut-data-mongodb:nativeCompile -x test -x internalStartTestResourcesService --no-daemon -FROM cgr.dev/chainguard/wolfi-base:latest WORKDIR /micronaut -COPY --from=build /home/gradle/src/micronaut-data-mongodb/build/native/nativeCompile/micronaut-data-mongodb micronaut +RUN mv /home/gradle/src/micronaut-data-mongodb/build/native/nativeCompile/micronaut-data-mongodb micronaut EXPOSE 8080 ENV MICRONAUT_ENVIRONMENTS=benchmark diff --git a/frameworks/Java/micronaut/micronaut-data-mongodb.dockerfile b/frameworks/Java/micronaut/micronaut-data-mongodb.dockerfile index f098cf335eb..16c0061d17a 100644 --- a/frameworks/Java/micronaut/micronaut-data-mongodb.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-mongodb.dockerfile @@ -3,7 +3,7 @@ COPY --chown=gradle:gradle . /home/gradle/src WORKDIR /home/gradle/src RUN gradle micronaut-data-mongodb:build -x test -x internalStartTestResourcesService --no-daemon -FROM openjdk:21 +FROM openjdk:23 WORKDIR /micronaut COPY --from=build /home/gradle/src/micronaut-data-mongodb/build/libs/micronaut-data-mongodb-all.jar micronaut.jar COPY run_benchmark.sh run_benchmark.sh diff --git a/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile index 6ada77518a5..614967b3379 100644 --- a/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile @@ -1,12 +1,11 @@ -FROM ghcr.io/graalvm/native-image-community:21-ol9 as build +FROM container-registry.oracle.com/graalvm/native-image:21 RUN microdnf install findutils # Gradle 8.7 requires xargs COPY . /home/gradle/src WORKDIR /home/gradle/src RUN ./gradlew micronaut-data-r2dbc:nativeCompile -x test -x internalStartTestResourcesService --no-daemon -FROM cgr.dev/chainguard/wolfi-base:latest WORKDIR /micronaut -COPY --from=build /home/gradle/src/micronaut-data-r2dbc/build/native/nativeCompile/micronaut-data-r2dbc micronaut +RUN mv /home/gradle/src/micronaut-data-r2dbc/build/native/nativeCompile/micronaut-data-r2dbc micronaut EXPOSE 8080 ENV MICRONAUT_ENVIRONMENTS=benchmark diff --git a/frameworks/Java/micronaut/micronaut-data-r2dbc.dockerfile b/frameworks/Java/micronaut/micronaut-data-r2dbc.dockerfile index 0fe88905ef3..f8a07dc83a0 100644 --- a/frameworks/Java/micronaut/micronaut-data-r2dbc.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-r2dbc.dockerfile @@ -3,7 +3,7 @@ COPY --chown=gradle:gradle . /home/gradle/src WORKDIR /home/gradle/src RUN gradle micronaut-data-r2dbc:build -x test -x internalStartTestResourcesService --no-daemon -FROM openjdk:21 +FROM openjdk:23 WORKDIR /micronaut COPY --from=build /home/gradle/src/micronaut-data-r2dbc/build/libs/micronaut-data-r2dbc-all.jar micronaut.jar COPY run_benchmark.sh run_benchmark.sh diff --git a/frameworks/Java/micronaut/micronaut-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-graalvm.dockerfile index 6a9208967cc..bbef52f862a 100644 --- a/frameworks/Java/micronaut/micronaut-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-graalvm.dockerfile @@ -1,13 +1,11 @@ -FROM ghcr.io/graalvm/native-image-community:21-ol9 as build +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 RUN ./gradlew micronaut-vertx-pg-client:nativeCompile -x test -x internalStartTestResourcesService --no-daemon -FROM cgr.dev/chainguard/wolfi-base:latest -RUN apk --no-cache update && apk add libstdc++ WORKDIR /micronaut -COPY --from=build /home/gradle/src/micronaut-vertx-pg-client/build/native/nativeCompile/micronaut-vertx-pg-client micronaut +RUN mv /home/gradle/src/micronaut-vertx-pg-client/build/native/nativeCompile/micronaut-vertx-pg-client micronaut EXPOSE 8080 ENV MICRONAUT_ENVIRONMENTS=benchmark diff --git a/frameworks/Java/micronaut/micronaut-jdbc-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-jdbc-graalvm.dockerfile index 27cb088bb04..671d236193d 100644 --- a/frameworks/Java/micronaut/micronaut-jdbc-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-jdbc-graalvm.dockerfile @@ -1,12 +1,11 @@ -FROM ghcr.io/graalvm/native-image-community:21-ol9 as build +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 RUN ./gradlew micronaut-jdbc:nativeCompile -x test -x internalStartTestResourcesService --no-daemon -FROM cgr.dev/chainguard/wolfi-base:latest WORKDIR /micronaut -COPY --from=build /home/gradle/src/micronaut-jdbc/build/native/nativeCompile/micronaut-jdbc micronaut +RUN mv /home/gradle/src/micronaut-jdbc/build/native/nativeCompile/micronaut-jdbc micronaut EXPOSE 8080 ENV MICRONAUT_ENVIRONMENTS=benchmark diff --git a/frameworks/Java/micronaut/micronaut-jdbc.dockerfile b/frameworks/Java/micronaut/micronaut-jdbc.dockerfile index 63b613d4e61..d6a026f0778 100644 --- a/frameworks/Java/micronaut/micronaut-jdbc.dockerfile +++ b/frameworks/Java/micronaut/micronaut-jdbc.dockerfile @@ -3,7 +3,7 @@ COPY --chown=gradle:gradle . /home/gradle/src WORKDIR /home/gradle/src RUN gradle micronaut-jdbc:build -x test -x internalStartTestResourcesService --no-daemon -FROM openjdk:21 +FROM openjdk:23 WORKDIR /micronaut COPY --from=build /home/gradle/src/micronaut-jdbc/build/libs/micronaut-jdbc-all.jar micronaut.jar COPY run_benchmark.sh run_benchmark.sh diff --git a/frameworks/Java/micronaut/micronaut-jdbc/src/main/java/benchmark/JdbcFortuneRepository.java b/frameworks/Java/micronaut/micronaut-jdbc/src/main/java/benchmark/JdbcFortuneRepository.java index 8b07fd1c6fc..2315804f7c2 100644 --- a/frameworks/Java/micronaut/micronaut-jdbc/src/main/java/benchmark/JdbcFortuneRepository.java +++ b/frameworks/Java/micronaut/micronaut-jdbc/src/main/java/benchmark/JdbcFortuneRepository.java @@ -42,7 +42,7 @@ public void initDb(Collection fortunes) { } @Override - public Collection findAll() { + public List findAll() { try (Connection connection = dataSource.getConnection()) { try (PreparedStatement statement = connection.prepareStatement("SELECT id, message FROM fortune", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) { diff --git a/frameworks/Java/micronaut/micronaut-r2dbc-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-r2dbc-graalvm.dockerfile index ff10709dae7..eaf2985de2d 100644 --- a/frameworks/Java/micronaut/micronaut-r2dbc-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-r2dbc-graalvm.dockerfile @@ -1,12 +1,11 @@ -FROM ghcr.io/graalvm/native-image-community:21-ol9 as build +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 RUN ./gradlew micronaut-r2dbc:nativeCompile -x test --no-daemon -FROM cgr.dev/chainguard/wolfi-base:latest WORKDIR /micronaut -COPY --from=build /home/gradle/src/micronaut-r2dbc/build/native/nativeCompile/micronaut-r2dbc micronaut +RUN mv /home/gradle/src/micronaut-r2dbc/build/native/nativeCompile/micronaut-r2dbc micronaut EXPOSE 8080 ENV MICRONAUT_ENVIRONMENTS=benchmark diff --git a/frameworks/Java/micronaut/micronaut-r2dbc.dockerfile b/frameworks/Java/micronaut/micronaut-r2dbc.dockerfile index 480b47cffa2..e0fd59fc36b 100644 --- a/frameworks/Java/micronaut/micronaut-r2dbc.dockerfile +++ b/frameworks/Java/micronaut/micronaut-r2dbc.dockerfile @@ -3,7 +3,7 @@ COPY --chown=gradle:gradle . /home/gradle/src WORKDIR /home/gradle/src RUN gradle micronaut-r2dbc:build -x test -x internalStartTestResourcesService --no-daemon -FROM openjdk:21 +FROM openjdk:23 WORKDIR /micronaut COPY --from=build /home/gradle/src/micronaut-r2dbc/build/libs/micronaut-r2dbc-all.jar micronaut.jar COPY run_benchmark.sh run_benchmark.sh 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/BenchmarkService.java b/frameworks/Java/redkale/BenchmarkService.java deleted file mode 100644 index 06e760bf879..00000000000 --- a/frameworks/Java/redkale/BenchmarkService.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkalex.benchmark; - -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.Stream; -import org.redkale.annotation.*; -import org.redkale.net.http.*; -import org.redkale.service.AbstractService; -import org.redkale.source.DataSource; - -/** - * 测试redkale-jdbc, 需要覆盖到原BenchmarkService - * - * @author zhangjx - */ -@RestService(name = " ", repair = false) -public class BenchmarkService extends AbstractService { - - private static final byte[] helloBytes = "Hello, world!".getBytes(); - - @Resource - private DataSource source; - - @NonBlocking - @RestMapping(auth = false) - public byte[] plaintext() { - return helloBytes; - } - - @NonBlocking - @RestMapping(auth = false) - public Message json() { - return new Message("Hello, World!"); - } - - @RestMapping(auth = false) - public World db() { - return source.find(World.class, ThreadLocalRandom.current().nextInt(10000) + 1); - } - - @RestMapping(auth = false) - public List queries(int q) { - return source.findsList(World.class, random(q)); - } - - @RestMapping(auth = false) - public List updates(int q) { - int size = Math.min(500, Math.max(1, q)); - int[] newNumbers = ThreadLocalRandom.current().ints(size, 1, 10001).toArray(); - List words = source.findsList(World.class, random(q)); - source.update(World.updateNewNumbers(words, newNumbers)); - return words; - } - - @RestMapping(auth = false) - public HttpScope fortunes() { - List fortunes = source.queryList(Fortune.class); - fortunes.add(new Fortune(0, "Additional fortune added at request time.")); - Collections.sort(fortunes); - return HttpScope.refer("").referObj(fortunes); - } - - @NonBlocking - @RestMapping(name = "cached-worlds", auth = false) - public CachedWorld[] cachedWorlds(int q) { - return source.finds(CachedWorld.class, random(q)); - } - - private Stream random(int q) { - int size = Math.min(500, Math.max(1, q)); - return ThreadLocalRandom.current().ints(size, 1, 10001).boxed(); - } -} diff --git a/frameworks/Java/redkale/benchmark_config.json b/frameworks/Java/redkale/benchmark_config.json index eb9e8d92ba2..52f8550ce9f 100644 --- a/frameworks/Java/redkale/benchmark_config.json +++ b/frameworks/Java/redkale/benchmark_config.json @@ -26,30 +26,6 @@ "notes": "", "versus": "Redkale" }, - "graalvm": { - "plaintext_url": "/plaintext", - "json_url": "/json", - "db_url": "/db", - "query_url": "/queries?q=", - "fortune_url": "/fortunes", - "update_url": "/updates?q=", - "cached_query_url": "/cached-worlds?q=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "Redkale", - "language": "Java", - "flavor": "None", - "orm": "Raw", - "platform": "Redkale", - "webserver": "Redkale", - "os": "Linux", - "database_os": "Linux", - "display_name": "redkale-graalvm", - "notes": "", - "versus": "Redkale" - }, "native": { "plaintext_url": "/plaintext", "json_url": "/json", @@ -95,28 +71,7 @@ "notes": "", "versus": "Redkale" }, - "block": { - "db_url": "/db", - "query_url": "/queries?q=", - "fortune_url": "/fortunes", - "update_url": "/updates?q=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "Redkale", - "language": "Java", - "flavor": "None", - "orm": "Raw", - "platform": "Redkale", - "webserver": "Redkale", - "os": "Linux", - "database_os": "Linux", - "display_name": "redkale-block", - "notes": "", - "versus": "Redkale" - }, - "vertx": { + "pgclient": { "db_url": "/db", "query_url": "/queries?q=", "fortune_url": "/fortunes", @@ -133,7 +88,7 @@ "webserver": "Redkale", "os": "Linux", "database_os": "Linux", - "display_name": "redkale-vertx", + "display_name": "redkale-pgclient", "notes": "", "versus": "Redkale" } diff --git a/frameworks/Java/redkale/conf/application.xml b/frameworks/Java/redkale/conf/application.xml index d7157c9f8d3..642b3da5928 100644 --- a/frameworks/Java/redkale/conf/application.xml +++ b/frameworks/Java/redkale/conf/application.xml @@ -4,19 +4,19 @@ - - + - - + + + diff --git a/frameworks/Java/redkale/conf/source.properties b/frameworks/Java/redkale/conf/source.properties index 9efb8cd4158..f88e22a2a12 100644 --- a/frameworks/Java/redkale/conf/source.properties +++ b/frameworks/Java/redkale/conf/source.properties @@ -4,5 +4,6 @@ redkale.datasource[].url = jdbc:postgresql://tfb-database:5432/hello_world redkale.datasource[].user = benchmarkdbuser redkale.datasource[].password = benchmarkdbpass -redkale.datasource[].warnslowms = 0 -redkale.datasource[].errorslowms = 0 +redkale.datasource[].non-blocking = true +redkale.datasource[].warn-slowms = 0 +redkale.datasource[].error-slowms = 0 diff --git a/frameworks/Java/redkale/config.toml b/frameworks/Java/redkale/config.toml index 73c0a6678e3..149aaad007e 100644 --- a/frameworks/Java/redkale/config.toml +++ b/frameworks/Java/redkale/config.toml @@ -19,24 +19,6 @@ platform = "Redkale" webserver = "Redkale" versus = "Redkale" -[graalvm] -urls.plaintext = "/plaintext" -urls.json = "/json" -urls.db = "/db" -urls.fortune = "/fortunes" -urls.query = "/queries?q=" -urls.update = "/updates?q=" -urls.cached_query = "/cached-worlds?q=" -approach = "Realistic" -classification = "Fullstack" -database = "Postgres" -database_os = "Linux" -os = "Linux" -orm = "Raw" -platform = "Redkale" -webserver = "Redkale" -versus = "Redkale" - [native] urls.plaintext = "/plaintext" urls.json = "/json" @@ -70,22 +52,7 @@ platform = "Redkale" webserver = "Redkale" versus = "Redkale" -[block] -urls.db = "/db" -urls.fortune = "/fortunes" -urls.query = "/queries?q=" -urls.update = "/updates?q=" -approach = "Realistic" -classification = "Fullstack" -database = "Postgres" -database_os = "Linux" -os = "Linux" -orm = "Raw" -platform = "Redkale" -webserver = "Redkale" -versus = "Redkale" - -[vertx] +[pgclient] urls.db = "/db" urls.fortune = "/fortunes" urls.query = "/queries?q=" diff --git a/frameworks/Java/redkale/pom-jdbc.xml b/frameworks/Java/redkale/pom-jdbc.xml index 43ce0fa4e0f..86b6c6d5fd6 100644 --- a/frameworks/Java/redkale/pom-jdbc.xml +++ b/frameworks/Java/redkale/pom-jdbc.xml @@ -8,10 +8,11 @@ org.redkale.boot.Application 2.8.0-SNAPSHOT - 42.6.0 + 1.2.0-SNAPSHOT + 42.7.2 UTF-8 - 18 - 18 + 21 + 21 @@ -76,7 +77,6 @@ UTF-8 -parameters - --enable-preview true @@ -85,7 +85,7 @@ org.redkale.maven.plugins redkale-maven-plugin - 1.2.0-SNAPSHOT + ${redkale-maven.version} --no-fallback diff --git a/frameworks/Java/redkale/pom-vertx.xml b/frameworks/Java/redkale/pom-pgclient.xml similarity index 93% rename from frameworks/Java/redkale/pom-vertx.xml rename to frameworks/Java/redkale/pom-pgclient.xml index 9879cff1237..47f212a3053 100644 --- a/frameworks/Java/redkale/pom-vertx.xml +++ b/frameworks/Java/redkale/pom-pgclient.xml @@ -8,11 +8,12 @@ org.redkale.boot.Application 2.8.0-SNAPSHOT - 4.5.0 + 1.2.0-SNAPSHOT + 4.5.8 2.1 UTF-8 - 18 - 18 + 21 + 21 @@ -83,7 +84,6 @@ UTF-8 -parameters - --enable-preview true @@ -92,7 +92,7 @@ org.redkale.maven.plugins redkale-maven-plugin - 1.2.0-SNAPSHOT + ${redkale-maven.version} --no-fallback diff --git a/frameworks/Java/redkale/pom.xml b/frameworks/Java/redkale/pom.xml index 8e6fa747ac0..3c7970fb4e3 100644 --- a/frameworks/Java/redkale/pom.xml +++ b/frameworks/Java/redkale/pom.xml @@ -8,9 +8,10 @@ org.redkale.boot.Application 2.8.0-SNAPSHOT + 1.2.0-SNAPSHOT UTF-8 - 11 - 11 + 21 + 21 @@ -74,10 +75,12 @@ org.redkale.maven.plugins redkale-maven-plugin - 1.2.0-SNAPSHOT + ${redkale-maven.version} --no-fallback + -J-XX:+UseNUMA + -J-XX:+UseParallelGC diff --git a/frameworks/Java/redkale/redkale-block.dockerfile b/frameworks/Java/redkale/redkale-block.dockerfile deleted file mode 100644 index 67594021962..00000000000 --- a/frameworks/Java/redkale/redkale-block.dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM maven:3.8.6-openjdk-18-slim as maven -WORKDIR /redkale -COPY src src -COPY conf conf -COPY pom.xml pom.xml -COPY BenchmarkService.java src/main/java/org/redkalex/benchmark/BenchmarkService.java -RUN mvn package -q - -FROM openjdk:21-jdk-slim -WORKDIR /redkale -COPY conf conf -COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark.jar - -EXPOSE 8080 - -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] \ No newline at end of file diff --git a/frameworks/Java/redkale/redkale-graalvm.dockerfile b/frameworks/Java/redkale/redkale-graalvm.dockerfile deleted file mode 100644 index 25ad428f6c8..00000000000 --- a/frameworks/Java/redkale/redkale-graalvm.dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM maven:3.8.6-openjdk-18-slim as maven -WORKDIR /redkale -COPY src src -COPY conf conf -COPY pom.xml pom.xml -RUN mvn package -q - - -FROM ghcr.io/graalvm/jdk-community:21.0.0 -WORKDIR /redkale -COPY conf conf -COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark.jar - -EXPOSE 8080 - -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] diff --git a/frameworks/Java/redkale/redkale-jdbc.dockerfile b/frameworks/Java/redkale/redkale-jdbc.dockerfile index c6549abfa03..ac3a85bec8b 100644 --- a/frameworks/Java/redkale/redkale-jdbc.dockerfile +++ b/frameworks/Java/redkale/redkale-jdbc.dockerfile @@ -1,16 +1,15 @@ -FROM maven:3.8.6-openjdk-18-slim as maven +FROM maven:3.9.6-amazoncorretto-21-debian as maven WORKDIR /redkale COPY src src COPY conf conf COPY pom-jdbc.xml pom.xml -COPY BenchmarkService.java src/main/java/org/redkalex/benchmark/BenchmarkService.java RUN mvn package -q -FROM openjdk:21-jdk-slim +FROM openjdk:23-jdk-slim WORKDIR /redkale COPY conf conf COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark.jar EXPOSE 8080 -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] \ No newline at end of file +CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] \ No newline at end of file diff --git a/frameworks/Java/redkale/redkale-native.dockerfile b/frameworks/Java/redkale/redkale-native.dockerfile index ae8e0cd7895..56fa1921f96 100644 --- a/frameworks/Java/redkale/redkale-native.dockerfile +++ b/frameworks/Java/redkale/redkale-native.dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.8.6-openjdk-18-slim as maven +FROM maven:3.9.6-amazoncorretto-21-debian as maven WORKDIR /redkale COPY src src COPY conf conf @@ -6,15 +6,15 @@ COPY pom.xml pom.xml RUN mvn package -q -FROM ghcr.io/graalvm/graalvm-ce:ol9-java17-22.3.3 -RUN gu install native-image +FROM ghcr.io/graalvm/native-image-community:22.0.2 as native WORKDIR /redkale COPY conf conf COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark.jar - RUN native-image -H:+ReportExceptionStackTraces --report-unsupported-elements-at-runtime -jar redkale-benchmark.jar -RUN ls -lh +FROM ghcr.io/graalvm/jdk-community:22.0.2 +WORKDIR /redkale +COPY --from=native /redkale/redkale-benchmark redkale-benchmark EXPOSE 8080 diff --git a/frameworks/Java/redkale/redkale-pgclient.dockerfile b/frameworks/Java/redkale/redkale-pgclient.dockerfile new file mode 100644 index 00000000000..af47b952c72 --- /dev/null +++ b/frameworks/Java/redkale/redkale-pgclient.dockerfile @@ -0,0 +1,15 @@ +FROM maven:3.9.6-amazoncorretto-21-debian as maven +WORKDIR /redkale +COPY src src +COPY conf conf +COPY pom-pgclient.xml pom.xml +RUN mvn package -q + +FROM openjdk:23-jdk-slim +WORKDIR /redkale +COPY conf conf +COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark.jar + +EXPOSE 8080 + +CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] \ No newline at end of file diff --git a/frameworks/Java/redkale/redkale-vertx.dockerfile b/frameworks/Java/redkale/redkale-vertx.dockerfile deleted file mode 100644 index f4099b6a776..00000000000 --- a/frameworks/Java/redkale/redkale-vertx.dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM maven:3.8.6-openjdk-18-slim as maven -WORKDIR /redkale -COPY src src -COPY conf conf -COPY pom-vertx.xml pom.xml -RUN mvn package -q - -FROM openjdk:21-jdk-slim -WORKDIR /redkale -COPY conf conf -COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark.jar - -EXPOSE 8080 - -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] \ No newline at end of file diff --git a/frameworks/Java/redkale/redkale.dockerfile b/frameworks/Java/redkale/redkale.dockerfile index d7878808a4b..79ec585c205 100644 --- a/frameworks/Java/redkale/redkale.dockerfile +++ b/frameworks/Java/redkale/redkale.dockerfile @@ -1,15 +1,15 @@ -FROM maven:3.8.6-openjdk-18-slim as maven +FROM maven:3.9.6-amazoncorretto-21-debian as maven WORKDIR /redkale COPY src src COPY conf conf COPY pom.xml pom.xml RUN mvn package -q -FROM openjdk:21-jdk-slim +FROM openjdk:23-jdk-slim WORKDIR /redkale COPY conf conf COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark.jar EXPOSE 8080 -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] +CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] diff --git a/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/BenchmarkService.java b/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/BenchmarkService.java index 20f816362d2..e2ebc7b8aad 100644 --- a/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/BenchmarkService.java +++ b/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/BenchmarkService.java @@ -7,8 +7,7 @@ import java.util.*; import java.util.concurrent.*; -import java.util.stream.IntStream; -import java.util.stream.Stream; +import java.util.stream.*; import org.redkale.annotation.*; import org.redkale.net.http.*; import org.redkale.service.AbstractService; @@ -53,8 +52,8 @@ public CompletableFuture> updates(int q) { IntStream ids = ThreadLocalRandom.current().ints(size, 1, 10001); int[] newNumbers = ThreadLocalRandom.current().ints(size, 1, 10001).toArray(); return source.findsListAsync(World.class, ids.boxed()) - .thenCompose(words -> source.updateAsync(World.updateNewNumbers(words, newNumbers)) - .thenApply(v -> words)); + .thenCompose(words -> source.updateAsync(World.updateNewNumbers(words, newNumbers)) + .thenApply(v -> words)); } @RestMapping(auth = false) diff --git a/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/FortuneRender.java b/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/FortuneRender.java index 201c58bc741..651864d0da5 100644 --- a/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/FortuneRender.java +++ b/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/FortuneRender.java @@ -7,6 +7,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.function.Function; import org.redkale.convert.Convert; import org.redkale.net.http.*; import org.redkale.util.*; @@ -19,7 +20,9 @@ public class FortuneRender implements org.redkale.net.http.HttpRender { private static final String contentType = "text/html; charset=utf-8"; - private static final byte[] text1 = "Fortunes".getBytes(StandardCharsets.UTF_8); + private static final byte[] text1 = + "Fortunes
idmessage
" + .getBytes(StandardCharsets.UTF_8); private static final byte[] text2 = "
idmessage
".getBytes(StandardCharsets.UTF_8); @@ -29,9 +32,9 @@ public class FortuneRender implements org.redkale.net.http.HttpRender { private static final byte[] text5 = "
".getBytes(StandardCharsets.UTF_8); - private final ThreadLocal localByteArray = ThreadLocal.withInitial(() -> new ByteArray(1280)); + private static final String arrayName = "fortuneByteArray"; - private final ThreadLocal localTmpArray = ThreadLocal.withInitial(() -> new ByteArray(128)); + private static final Function arrayFunc = s -> new ByteArray(1280); private byte[][] idBytesCache; @@ -53,12 +56,11 @@ public void init(HttpContext context, AnyValue config) { @Override public void renderTo(HttpRequest request, HttpResponse response, Convert convert, HttpScope scope) { - ByteArray array = localByteArray.get().clear(); + ByteArray array = request.getSubobjectIfAbsent(arrayName, arrayFunc).clear(); array.put(text1); - ByteArray tmp = localTmpArray.get(); for (Fortune item : (List) scope.getReferObj()) { - array.put(text2).put(escapeId(item.getId())) - .put(text3).put(escapeMessage(tmp, item.getMessage())).put(text4); + ByteArray msg = request.getSubobjectIfAbsent(item.getMessage(), this::escapeMessage); + array.put(text2).put(escapeId(item.getId())).put(text3).put(msg).put(text4); } array.put(text5); response.finish(contentType, array); @@ -72,23 +74,22 @@ private byte[] escapeId(int id) { } } - private ByteArray escapeMessage(ByteArray tmp, String message) { - tmp.clear(); + private ByteArray escapeMessage(String message) { + ByteArray array = new ByteArray(128); CharSequence cs = message; for (int i = 0; i < cs.length(); i++) { char c = cs.charAt(i); byte[] bs = c < escapeCache.length ? escapeCache[c] : null; if (bs != null) { - tmp.put(bs); + array.put(bs); } else if (c < 0x80) { - tmp.put((byte) c); + array.put((byte) c); } else if (c < 0x800) { - tmp.put((byte) (0xc0 | (c >> 6)), (byte) (0x80 | (c & 0x3f))); + array.put((byte) (0xc0 | (c >> 6)), (byte) (0x80 | (c & 0x3f))); } else { - tmp.put((byte) (0xe0 | ((c >> 12))), (byte) (0x80 | ((c >> 6) & 0x3f)), (byte) (0x80 | (c & 0x3f))); + array.put((byte) (0xe0 | (c >> 12)), (byte) (0x80 | ((c >> 6) & 0x3f)), (byte) (0x80 | (c & 0x3f))); } } - return tmp; + return array; } - } diff --git a/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/Message.java b/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/Message.java index 73cbe2c2afd..a5e7aed358d 100644 --- a/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/Message.java +++ b/frameworks/Java/redkale/src/main/java/org/redkalex/benchmark/Message.java @@ -1,41 +1,40 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkalex.benchmark; - -import org.redkale.annotation.Bean; -import org.redkale.convert.ConvertSmallString; -import org.redkale.convert.json.JsonConvert; - -/** - * - * @author zhangjx - */ -@Bean -public final class Message { - - @ConvertSmallString - private String message; - - public Message() { - } - - public Message(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkalex.benchmark; + +import org.redkale.annotation.Serial; +import org.redkale.convert.ConvertStandardString; +import org.redkale.convert.json.JsonConvert; + +/** + * + * @author zhangjx + */ +@Serial +public final class Message { + + @ConvertStandardString + private String message; + + public Message() {} + + public Message(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/frameworks/Java/solon-vertx/README.md b/frameworks/Java/solon-vertx/README.md new file mode 100644 index 00000000000..40b8ab3b2cf --- /dev/null +++ b/frameworks/Java/solon-vertx/README.md @@ -0,0 +1,23 @@ +# solon-vertx Benchmarking Test + + +This is the solon-vertx portion of a [benchmarking test suite](../) comparing a variety of web development platforms. + +### JSON Encoding Test +* [JSON test source](src/main/java/hello/Main.java) +* [Plaintext test source](src/main/java/hello/Main.java) + +## Versions + +* [Java OpenJDK 21](http://openjdk.java.net/) +* [solon 3.0.2](https://github.com/noear/solon) + +## Test URLs + +### JSON Encoding Test + + http://localhost:8080/json + +### Plaintext Encoding Test + + http://localhost:8080/plaintext \ No newline at end of file diff --git a/frameworks/Java/solon-vertx/benchmark_config.json b/frameworks/Java/solon-vertx/benchmark_config.json new file mode 100644 index 00000000000..3b7de26f5d4 --- /dev/null +++ b/frameworks/Java/solon-vertx/benchmark_config.json @@ -0,0 +1,26 @@ +{ + "framework": "solon-vertx", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "None", + "framework": "solon", + "language": "Java", + "flavor": "None", + "orm": "Micro", + "platform": "Netty", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "solon-vertx", + "notes": "", + "versus": "solon" + } + } + ] +} diff --git a/frameworks/Java/solon-vertx/config.toml b/frameworks/Java/solon-vertx/config.toml new file mode 100644 index 00000000000..8306ab94cd1 --- /dev/null +++ b/frameworks/Java/solon-vertx/config.toml @@ -0,0 +1,15 @@ +[framework] +name = "solon-vertx" + +[main] +urls.plaintext = "/plaintext" +urls.json = "/json" +approach = "Realistic" +classification = "Platform" +database = "None" +database_os = "Linux" +os = "Linux" +orm = "Micro" +platform = "Netty" +webserver = "None" +versus = "solon" diff --git a/frameworks/Java/solon-vertx/pom.xml b/frameworks/Java/solon-vertx/pom.xml new file mode 100644 index 00000000000..2546baecf35 --- /dev/null +++ b/frameworks/Java/solon-vertx/pom.xml @@ -0,0 +1,61 @@ + + 4.0.0 + + + org.noear + solon-parent + 3.0.2 + + + hello + hello-solon + 1.0 + jar + + + 21 + + + + + org.noear + solon-boot-vertx + + + + org.noear + solon-serialization-jackson + + + + + ${project.artifactId} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + hello.Main + + + + + + make-assembly + package + + single + + + + + + + diff --git a/frameworks/Java/solon-vertx/solon-vertx.dockerfile b/frameworks/Java/solon-vertx/solon-vertx.dockerfile new file mode 100644 index 00000000000..c387938661c --- /dev/null +++ b/frameworks/Java/solon-vertx/solon-vertx.dockerfile @@ -0,0 +1,13 @@ +FROM maven:3.9.7-amazoncorretto-21 as maven +WORKDIR /solon +COPY pom.xml pom.xml +COPY src src +RUN mvn compile assembly:single -q + +FROM openjdk:21-jdk-slim +WORKDIR /solon +COPY --from=maven /solon/target/hello-solon.jar app.jar + +EXPOSE 8080 + +CMD ["java", "-server", "-cp", "app.jar", "hello.Main"] \ No newline at end of file diff --git a/frameworks/Java/solon-vertx/src/main/java/hello/Main.java b/frameworks/Java/solon-vertx/src/main/java/hello/Main.java new file mode 100644 index 00000000000..bc6467e87af --- /dev/null +++ b/frameworks/Java/solon-vertx/src/main/java/hello/Main.java @@ -0,0 +1,13 @@ +package hello; + +import org.noear.solon.Solon; + +/** + * @author noear + * @version V1.0 + */ +public class Main { + public static void main(String[] args) { + Solon.start(Main.class, args); + } +} diff --git a/frameworks/Java/solon-vertx/src/main/java/hello/controller/FilterImpl.java b/frameworks/Java/solon-vertx/src/main/java/hello/controller/FilterImpl.java new file mode 100644 index 00000000000..7fb22e497f8 --- /dev/null +++ b/frameworks/Java/solon-vertx/src/main/java/hello/controller/FilterImpl.java @@ -0,0 +1,23 @@ +package hello.controller; + +import org.noear.solon.annotation.Component; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.handle.FilterChain; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +@Component +public class FilterImpl implements Filter { + private static DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, d MMM yyyyy HH:mm:ss z"); + + @Override + public void doFilter(Context ctx, FilterChain chain) throws Throwable { + String dateString = DATE_FORMAT.format(new Date()); + ctx.headerSet("Date", dateString); + ctx.headerSet("Server", "solon-boot-vertx"); + chain.doFilter(ctx); + } +} diff --git a/frameworks/Java/solon/src/main/java/pmg/controller/HelloController.java b/frameworks/Java/solon-vertx/src/main/java/hello/controller/HelloController.java similarity index 88% rename from frameworks/Java/solon/src/main/java/pmg/controller/HelloController.java rename to frameworks/Java/solon-vertx/src/main/java/hello/controller/HelloController.java index fe9a1b43e3a..77049043e97 100644 --- a/frameworks/Java/solon/src/main/java/pmg/controller/HelloController.java +++ b/frameworks/Java/solon-vertx/src/main/java/hello/controller/HelloController.java @@ -1,9 +1,9 @@ -package pmg.controller; +package hello.controller; import org.noear.solon.annotation.Controller; import org.noear.solon.annotation.Get; import org.noear.solon.annotation.Mapping; -import pmg.model.Message; +import hello.model.Message; /** * @author noear diff --git a/frameworks/Java/solon-vertx/src/main/java/hello/model/Message.java b/frameworks/Java/solon-vertx/src/main/java/hello/model/Message.java new file mode 100644 index 00000000000..235dd2d86dd --- /dev/null +++ b/frameworks/Java/solon-vertx/src/main/java/hello/model/Message.java @@ -0,0 +1,21 @@ +package hello.model; + +/** + * @author noear + * @version V1.0 + */ +public class Message { + private String message; + + public Message(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/frameworks/Java/solon-vertx/src/main/resources/app.properties b/frameworks/Java/solon-vertx/src/main/resources/app.properties new file mode 100644 index 00000000000..26a5df70351 --- /dev/null +++ b/frameworks/Java/solon-vertx/src/main/resources/app.properties @@ -0,0 +1 @@ +server.http.ioBound=false \ No newline at end of file diff --git a/frameworks/Java/solon/README.md b/frameworks/Java/solon/README.md index 0824b5effd9..49d4aef9fe8 100644 --- a/frameworks/Java/solon/README.md +++ b/frameworks/Java/solon/README.md @@ -4,13 +4,13 @@ This is the solon portion of a [benchmarking test suite](../) comparing a variety of web development platforms. ### JSON Encoding Test -* [JSON test source](src/main/java/pmg/Main.java) -* [Plaintext test source](src/main/java/pmg/Main.java) +* [JSON test source](src/main/java/hello/Main.java) +* [Plaintext test source](src/main/java/hello/Main.java) ## Versions -* [Java OpenJDK 1.8](http://openjdk.java.net/) -* [solon 2.0.0](https://github.com/noear/solon) +* [Java OpenJDK 21](http://openjdk.java.net/) +* [solon 3.0.2](https://github.com/noear/solon) ## Test URLs diff --git a/frameworks/Java/solon/pom.xml b/frameworks/Java/solon/pom.xml index 58e7d792301..428d95b35b7 100644 --- a/frameworks/Java/solon/pom.xml +++ b/frameworks/Java/solon/pom.xml @@ -2,91 +2,47 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.noear.solon - solon-benchmark + + org.noear + solon-parent + 3.0.2 + + + hello + hello-solon 1.0 jar - UTF-8 - 11 - 11 - 2.0.0 + 21 org.noear - solon.boot.smarthttp - ${solon.version} + solon-boot-smarthttp + org.noear - solon.serialization.snack3 - ${solon.version} + solon-serialization-jackson - - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - sonatype-nexus-snapshots - Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots - - - - - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - sonatype-nexus-snapshots - Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots - - false - - - true - - - - ${project.artifactId} - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - -parameters - 1.8 - 1.8 - UTF-8 - false - - - org.apache.maven.plugins maven-assembly-plugin - 3.3.0 jar-with-dependencies - pmg.Main + hello.Main diff --git a/frameworks/Java/solon/solon.dockerfile b/frameworks/Java/solon/solon.dockerfile index 21d7f50d8a8..c387938661c 100644 --- a/frameworks/Java/solon/solon.dockerfile +++ b/frameworks/Java/solon/solon.dockerfile @@ -1,13 +1,13 @@ -FROM maven:3.6.1-jdk-11-slim as maven +FROM maven:3.9.7-amazoncorretto-21 as maven WORKDIR /solon COPY pom.xml pom.xml COPY src src RUN mvn compile assembly:single -q -FROM openjdk:11.0.3-jdk-slim +FROM openjdk:21-jdk-slim WORKDIR /solon -COPY --from=maven /solon/target/solon-benchmark-jar-with-dependencies.jar app.jar +COPY --from=maven /solon/target/hello-solon.jar app.jar EXPOSE 8080 -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-cp", "app.jar", "pmg.Main"] +CMD ["java", "-server", "-cp", "app.jar", "hello.Main"] \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/java/pmg/Main.java b/frameworks/Java/solon/src/main/java/hello/Main.java similarity index 87% rename from frameworks/Java/solon/src/main/java/pmg/Main.java rename to frameworks/Java/solon/src/main/java/hello/Main.java index b4b7b651797..0746f86be45 100644 --- a/frameworks/Java/solon/src/main/java/pmg/Main.java +++ b/frameworks/Java/solon/src/main/java/hello/Main.java @@ -1,13 +1,13 @@ -package pmg; - -import org.noear.solon.Solon; - -/** - * @author pmg1991 - * @version V1.0 - */ -public class Main { - public static void main(String[] args) { - Solon.start(Main.class, args); - } -} +package hello; + +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/controller/HelloController.java b/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java new file mode 100644 index 00000000000..77049043e97 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java @@ -0,0 +1,25 @@ +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/pmg/model/Message.java b/frameworks/Java/solon/src/main/java/hello/model/Message.java similarity index 93% rename from frameworks/Java/solon/src/main/java/pmg/model/Message.java rename to frameworks/Java/solon/src/main/java/hello/model/Message.java index f88b9b1a8d4..1df5276ca8d 100644 --- a/frameworks/Java/solon/src/main/java/pmg/model/Message.java +++ b/frameworks/Java/solon/src/main/java/hello/model/Message.java @@ -1,4 +1,4 @@ -package pmg.model; +package hello.model; /** * @author pmg1991 diff --git a/frameworks/Java/spring-webflux/README.md b/frameworks/Java/spring-webflux/README.md index 91e18bc9ceb..74297391137 100755 --- a/frameworks/Java/spring-webflux/README.md +++ b/frameworks/Java/spring-webflux/README.md @@ -37,15 +37,6 @@ For mongoDB access, spring-data-mongodb with reactive support is used. See [Mong * [Template rendering test source](src/main/java/benchmark/web/WebfluxRouter.java) -## Versions - -* [Java OpenJDK 10](http://openjdk.java.net/) -* [Spring boot 2.1.0.RELEASE](https://spring.io/projects/spring-boot) -* [Spring data mongodb 2.1.0.RELEASE](https://projects.spring.io/spring-data-mongodb/) -* [reactive-pg-client 0.10.6](https://github.com/reactiverse/reactive-pg-client) -* [rxjava2-jdbc 0.2.0](https://github.com/davidmoten/rxjava2-jdbc) -* [r2dbc-postgresql 1.0.0.BUILD-SNAPSHOT](https://github.com/r2dbc/r2dbc-postgresql) - ## Test URLs ### Plaintext Test diff --git a/frameworks/Java/spring-webflux/benchmark_config.json b/frameworks/Java/spring-webflux/benchmark_config.json index e5c13f8e292..d3d0746d448 100644 --- a/frameworks/Java/spring-webflux/benchmark_config.json +++ b/frameworks/Java/spring-webflux/benchmark_config.json @@ -2,9 +2,11 @@ "framework": "spring-webflux", "tests": [{ "default": { + "json_url": "/json", "db_url": "/db", "query_url": "/queries?queries=", "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", "update_url": "/updates?queries=", "port": 8080, "approach": "Realistic", @@ -20,13 +22,14 @@ "database_os": "Linux", "display_name": "spring-webflux-r2dbc", "notes": "", - "versus": "spring", - "tags": ["broken"] + "versus": "spring" }, "mongo": { + "json_url": "/json", "db_url": "/db", "query_url": "/queries?queries=", "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", "port": 8080, "approach": "Realistic", "classification": "Fullstack", @@ -41,74 +44,7 @@ "database_os": "Linux", "display_name": "spring-webflux-mongo", "notes": "", - "versus": "spring", - "tags": ["broken"] - }, - "pgclient": { - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "spring", - "language": "Java", - "flavor": "None", - "orm": "Micro", - "platform": "Netty", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "spring-webflux-pgclient", - "notes": "", - "versus": "spring", - "tags": ["broken"] - }, - "rxjdbc": { - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "spring", - "language": "Java", - "flavor": "None", - "orm": "Micro", - "platform": "Netty", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "spring-webflux-rxjdbc", - "notes": "", - "versus": "spring", - "tags": ["broken"] - }, - "jdbc": { - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "spring", - "language": "Java", - "flavor": "None", - "orm": "Micro", - "platform": "Netty", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "spring-webflux-jdbc", - "notes": "", - "versus": "spring", - "tags": ["broken"] + "versus": "spring" } }] } diff --git a/frameworks/Java/spring-webflux/config.toml b/frameworks/Java/spring-webflux/config.toml index 0cf76705159..806591a8384 100644 --- a/frameworks/Java/spring-webflux/config.toml +++ b/frameworks/Java/spring-webflux/config.toml @@ -16,36 +16,6 @@ platform = "Netty" webserver = "None" versus = "spring" -[pgclient] -urls.db = "/db" -urls.query = "/queries?queries=" -urls.update = "/updates?queries=" -urls.fortune = "/fortunes" -approach = "Realistic" -classification = "Fullstack" -database = "Postgres" -database_os = "Linux" -os = "Linux" -orm = "Micro" -platform = "Netty" -webserver = "None" -versus = "spring" - -[jdbc] -urls.db = "/db" -urls.query = "/queries?queries=" -urls.update = "/updates?queries=" -urls.fortune = "/fortunes" -approach = "Realistic" -classification = "Fullstack" -database = "Postgres" -database_os = "Linux" -os = "Linux" -orm = "Micro" -platform = "Netty" -webserver = "None" -versus = "spring" - [mongo] urls.db = "/db" urls.query = "/queries?queries=" @@ -59,18 +29,3 @@ orm = "Full" platform = "Netty" webserver = "None" versus = "spring" - -[rxjdbc] -urls.db = "/db" -urls.query = "/queries?queries=" -urls.update = "/updates?queries=" -urls.fortune = "/fortunes" -approach = "Realistic" -classification = "Fullstack" -database = "Postgres" -database_os = "Linux" -os = "Linux" -orm = "Micro" -platform = "Netty" -webserver = "None" -versus = "spring" diff --git a/frameworks/Java/spring-webflux/pom.xml b/frameworks/Java/spring-webflux/pom.xml index 01d32c261c0..7e1b6865d2d 100644 --- a/frameworks/Java/spring-webflux/pom.xml +++ b/frameworks/Java/spring-webflux/pom.xml @@ -8,24 +8,19 @@ benchmark spring-webflux-benchmark - 1.1-SNAPSHOT + 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.2.0.M2 + 3.3.4 - 11 - 11 - UTF-8 - 1.0.0.M2 - 42.7.2 - 0.11.4 - 0.2.4 - 1.0.0.M7 - 1.0.0.BUILD-SNAPSHOT + 21 + 1.3.6 + 1.0.2.RELEASE + 1.0.7.RELEASE @@ -35,112 +30,30 @@ org.springframework.boot - spring-boot-starter-jdbc - - - org.springframework.boot - spring-boot-starter-mustache - - - org.springframework.boot - spring-boot-starter-data-mongodb-reactive + spring-boot-starter-data-r2dbc org.postgresql - postgresql - ${postgresql.version} - - - io.reactiverse - reactive-pg-client - ${pgclient.version} - - - com.github.davidmoten - rxjava2-jdbc - ${rxjava2-jdbc.version} - - - - io.r2dbc r2dbc-postgresql - ${r2dbc-postgresql.version} - - - io.r2dbc - r2dbc-pool - ${r2dbc-pool.version} - - - org.springframework.data - spring-data-r2dbc - ${spring-data-r2dbc.version} - - - - org.springframework - spring-tx - - - org.springframework - spring-context - - - org.springframework - spring-core - - - org.springframework - spring-beans - - - org.springframework - spring-tx - 5.2.0.M2 + io.jstach + jstachio + ${jstachio.version} - org.springframework - spring-context - 5.2.22.BUILD-SNAPSHOT + io.jstach + jstachio-apt + ${jstachio.version} + provided + true - org.springframework - spring-core - 5.2.24.RELEASE - - - org.springframework - spring-beans - 5.2.22.BUILD-SNAPSHOT + org.springframework.boot + spring-boot-starter-data-mongodb-reactive - - - - spring-libs-snapshot - Spring Snapshots - https://repo.spring.io/libs-snapshot - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - - - spring-libs-snapshot - Spring Snapshots - https://repo.spring.io/libs-snapshot - - - ${project.artifactId} @@ -151,9 +64,14 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 - false + + + io.jstach + jstachio-apt + ${jstachio.version} + + diff --git a/frameworks/Java/spring-webflux/spring-webflux-jdbc.dockerfile b/frameworks/Java/spring-webflux/spring-webflux-jdbc.dockerfile deleted file mode 100644 index 89f1ba40f89..00000000000 --- a/frameworks/Java/spring-webflux/spring-webflux-jdbc.dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM maven:3.8.5-openjdk-17-slim as maven -WORKDIR /spring -COPY src src -COPY pom.xml pom.xml -RUN mvn package -q - -FROM openjdk:17.0-jdk-slim -WORKDIR /spring -COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar - -EXPOSE 8080 - -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jdbc,postgres"] diff --git a/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile b/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile index 95d24ac6003..d565d1556c3 100644 --- a/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile +++ b/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile @@ -1,13 +1,16 @@ -FROM maven:3.8.5-openjdk-17-slim as maven +FROM maven:3.9.5-eclipse-temurin-21 as maven WORKDIR /spring COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM openjdk:17.0-jdk-slim +FROM bellsoft/liberica-openjre-debian:21 WORKDIR /spring COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar +# See https://docs.spring.io/spring-boot/reference/packaging/efficient.html +RUN java -Djarmode=tools -jar app.jar extract + EXPOSE 8080 -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=mongo"] +CMD ["java", "-Dlogging.level.root=OFF", "-Dio.netty.leakDetection.level=disabled", "-Dreactor.netty.http.server.lastFlushWhenNoRead=true", "-jar", "app/app.jar", "--spring.profiles.active=mongo"] \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/spring-webflux-pgclient.dockerfile b/frameworks/Java/spring-webflux/spring-webflux-pgclient.dockerfile deleted file mode 100644 index 0c03140d05a..00000000000 --- a/frameworks/Java/spring-webflux/spring-webflux-pgclient.dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM maven:3.8.5-openjdk-17-slim as maven -WORKDIR /spring -COPY src src -COPY pom.xml pom.xml -RUN mvn package -q - -FROM openjdk:17.0-jdk-slim -WORKDIR /spring -COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar - -EXPOSE 8080 - -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=pgclient,postgres"] diff --git a/frameworks/Java/spring-webflux/spring-webflux-rxjdbc.dockerfile b/frameworks/Java/spring-webflux/spring-webflux-rxjdbc.dockerfile deleted file mode 100644 index 05aacc1a8f1..00000000000 --- a/frameworks/Java/spring-webflux/spring-webflux-rxjdbc.dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM maven:3.8.5-openjdk-17-slim as maven -WORKDIR /spring -COPY src src -COPY pom.xml pom.xml -RUN mvn package -q - -FROM openjdk:17.0-jdk-slim -WORKDIR /spring -COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar - -EXPOSE 8080 - -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=rxjdbc,postgres"] diff --git a/frameworks/Java/spring-webflux/spring-webflux.dockerfile b/frameworks/Java/spring-webflux/spring-webflux.dockerfile index 3d41fc1ecbe..e1cee08ff31 100644 --- a/frameworks/Java/spring-webflux/spring-webflux.dockerfile +++ b/frameworks/Java/spring-webflux/spring-webflux.dockerfile @@ -1,13 +1,15 @@ -FROM maven:3.8.5-openjdk-17-slim as maven +FROM maven:3.9.5-eclipse-temurin-21 as maven WORKDIR /spring COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM openjdk:17.0-jdk-slim +FROM bellsoft/liberica-openjre-debian:21 WORKDIR /spring COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar +# See https://docs.spring.io/spring-boot/reference/packaging/efficient.html +RUN java -Djarmode=tools -jar app.jar extract EXPOSE 8080 -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=r2dbc,postgres"] +CMD ["java", "-Dlogging.level.root=OFF", "-Dio.netty.leakDetection.level=disabled", "-Dreactor.netty.http.server.lastFlushWhenNoRead=true", "-jar", "app/app.jar", "--spring.profiles.active=r2dbc"] diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/App.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/App.java old mode 100755 new mode 100644 index e33025461c6..3ba49056611 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/App.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/App.java @@ -1,53 +1,31 @@ package benchmark; -import org.springframework.beans.factory.annotation.Autowired; +import benchmark.web.ServerFilter; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.reactive.result.view.MustacheViewResolver; import org.springframework.context.annotation.Bean; +import org.springframework.http.server.reactive.HttpHandler; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.web.reactive.config.EnableWebFlux; -import org.springframework.web.reactive.config.ViewResolverRegistry; -import org.springframework.web.reactive.config.WebFluxConfigurer; -import reactor.core.scheduler.Scheduler; -import reactor.core.scheduler.Schedulers; - -import java.util.concurrent.Executors; - -@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class }) -@EnableWebFlux +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.WebHandler; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; + +@SpringBootApplication @EnableScheduling -@EnableConfigurationProperties -public class App implements WebFluxConfigurer { - - @Autowired - private MustacheViewResolver mustacheViewResolver; - - public static void main(String[] args) { - SpringApplication.run(App.class, args); - } +public class App { @Bean - public ServerFilter serverFilter() { - return new ServerFilter(); + public HttpHandler httpHandler(RouterFunction route, ServerFilter serverFilter) { + WebHandler webHandler = RouterFunctions.toWebHandler(route, HandlerStrategies.builder().build()); + return WebHttpHandlerBuilder.webHandler(webHandler).filter(serverFilter).build(); } - @Bean - public DateHandler dateHandler() { - return new DateHandler(); - } - - @Bean - public Scheduler ioScheduler() { - return Schedulers.fromExecutor(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2)); - } - - @Override - public void configureViewResolvers(ViewResolverRegistry registry) { - registry.viewResolver(mustacheViewResolver); + public static void main(String[] args) { + SpringApplication.run(App.class, args); } } \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/DateHandler.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/DateHandler.java deleted file mode 100644 index fdaab4a2209..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/DateHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package benchmark; - -import org.springframework.scheduling.annotation.Scheduled; - -import java.util.Date; - -public class DateHandler { - - private Date date = new Date(); - - @Scheduled(fixedRate = 1000) - public void update() { - this.date = new Date(); - } - - public Date getDate() { - return date; - } -} diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/PgClients.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/PgClients.java deleted file mode 100644 index 902478447c6..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/PgClients.java +++ /dev/null @@ -1,19 +0,0 @@ -package benchmark; - -import io.reactiverse.pgclient.PgClient; - -import java.util.Collection; -import java.util.Iterator; -import java.util.stream.Stream; - -public class PgClients { - private final Iterator iterator; - - public PgClients(Collection clients) { - iterator = Stream.generate(() -> clients).flatMap(Collection::stream).iterator(); - } - - public synchronized PgClient getOne() { - return iterator.next(); - } -} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/Utils.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/Utils.java new file mode 100644 index 00000000000..4631be27f11 --- /dev/null +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/Utils.java @@ -0,0 +1,19 @@ +package benchmark; + +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/spring-webflux/src/main/java/benchmark/config/JdbcConfig.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/config/JdbcConfig.java deleted file mode 100644 index cd97d6e9708..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/JdbcConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package benchmark.config; - -import com.zaxxer.hikari.HikariDataSource; -import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -import javax.sql.DataSource; - -@Configuration -@Profile("jdbc") -public class JdbcConfig { - - @Bean - public DataSource datasource(DataSourceProperties dataSourceProperties) { - HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); - dataSource.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() * 2); - - return dataSource; - } -} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/PgClientConfig.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/config/PgClientConfig.java deleted file mode 100644 index ebcbc732d67..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/PgClientConfig.java +++ /dev/null @@ -1,94 +0,0 @@ -package benchmark.config; - -import benchmark.PgClients; -import io.reactiverse.pgclient.PgClient; -import io.reactiverse.pgclient.PgPool; -import io.reactiverse.pgclient.PgPoolOptions; -import io.vertx.core.Vertx; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -import java.util.ArrayList; -import java.util.List; - -@Configuration -@Profile("pgclient") -@ConfigurationProperties(prefix = "database") -public class PgClientConfig { - private String name; - private String host; - private int port; - private String username; - private String password; - - @Bean - public Vertx vertx() { - return Vertx.vertx(); - } - - @Bean - public PgClients pgClients(Vertx vertx) { - List clients = new ArrayList<>(); - - for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) { - clients.add(pgClient(vertx)); - } - - return new PgClients(clients); - } - - - public PgPool pgClient(Vertx vertx) { - PgPoolOptions options = new PgPoolOptions(); - options.setDatabase(name); - options.setHost(host); - options.setPort(port); - options.setUser(username); - options.setPassword(password); - options.setCachePreparedStatements(true); - options.setMaxSize(1); - return PgClient.pool(vertx, options); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - 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; - } -} diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/R2dbcConfig.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/config/R2dbcConfig.java deleted file mode 100644 index 60e006cc80f..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/R2dbcConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package benchmark.config; - - -import io.r2dbc.spi.ConnectionFactories; -import io.r2dbc.spi.ConnectionFactory; - -import io.r2dbc.spi.ConnectionFactoryOptions; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.data.r2dbc.core.DatabaseClient; - -import static io.r2dbc.spi.ConnectionFactoryOptions.*; - -@Configuration -@Profile("r2dbc") -@ConfigurationProperties(prefix = "database") -public class R2dbcConfig { - private String name; - private String host; - private int port; - private String username; - private String password; - - public void setName(String name) { - this.name = name; - } - - public void setHost(String host) { - this.host = host; - } - - public void setPort(int port) { - this.port = port; - } - - public void setUsername(String username) { - this.username = username; - } - - public void setPassword(String password) { - this.password = password; - } - - @Bean - public ConnectionFactory connectionFactory() { - return ConnectionFactories.get(ConnectionFactoryOptions.builder() - .option(DRIVER,"pool") - .option(PROTOCOL,"postgresql") - .option(HOST, host) - .option(PORT, port) - .option(USER, username) - .option(PASSWORD, password) - .option(DATABASE, name) - .build()); - } - - @Bean - public DatabaseClient databaseClient(ConnectionFactory connectionFactory) { - return DatabaseClient.create(connectionFactory); - } -} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/ReactiveMongoConfig.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/config/ReactiveMongoConfig.java deleted file mode 100644 index ee80f0fd87b..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/ReactiveMongoConfig.java +++ /dev/null @@ -1,53 +0,0 @@ -package benchmark.config; - -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; -import org.slf4j.LoggerFactory; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; - -@Configuration -@EnableReactiveMongoRepositories -@Profile("mongo") -@ConfigurationProperties(prefix = "database") -public class ReactiveMongoConfig extends AbstractReactiveMongoConfiguration { - private String url; - private String name; - - @Bean - public MongoClient reactiveMongoClient() { - LoggerFactory.getLogger(getClass()).info("Connecting to mongo url: {}/{}", url, name); - return MongoClients.create(url); - } - - @Override - protected String getDatabaseName() { - return name; - } - - @Bean - public ReactiveMongoTemplate reactiveMongoTemplate() { - return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName()); - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/RxJdbcConfig.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/config/RxJdbcConfig.java deleted file mode 100644 index 23b7d200cca..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/config/RxJdbcConfig.java +++ /dev/null @@ -1,29 +0,0 @@ -package benchmark.config; - -import org.davidmoten.rx.jdbc.ConnectionProvider; -import org.davidmoten.rx.jdbc.Database; -import org.davidmoten.rx.jdbc.pool.NonBlockingConnectionPool; -import org.davidmoten.rx.jdbc.pool.Pools; -import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -import java.sql.SQLException; - -@Configuration -@Profile("rxjdbc") -public class RxJdbcConfig { - @Bean - public Database database(DataSourceProperties dsProps) throws SQLException { - NonBlockingConnectionPool pool = - Pools.nonBlocking() - .maxPoolSize(Runtime.getRuntime().availableProcessors() * 2) - .connectionProvider(ConnectionProvider.from(dsProps.getUrl(), dsProps.getUsername(), dsProps.getPassword())) - .build(); - - Database db = Database.from(pool); - - return db; - } -} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/model/Fortune.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/model/Fortune.java index 12ae17e0448..d66f56215fc 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/model/Fortune.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/model/Fortune.java @@ -4,9 +4,11 @@ import org.springframework.data.mongodb.core.mapping.Document; @Document -public final class Fortune { +public final class Fortune implements Comparable { + @Id public int id; + public String message; public Fortune(int id, String message) { @@ -14,11 +16,8 @@ public Fortune(int id, String message) { this.message = message; } - public int getId() { - return id; - } - - public String getMessage() { - return message; + @Override + public int compareTo(final Fortune other) { + return message.compareTo(other.message); } } diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/model/World.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/model/World.java index 612c7fef03a..ab096a1e313 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/model/World.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/model/World.java @@ -9,6 +9,7 @@ public final class World { @Id public int id; + @Field("randomNumber") public int randomnumber; 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 20e753e317e..54b6d0d9d02 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 @@ -6,6 +6,7 @@ import reactor.core.publisher.Mono; public interface DbRepository { + Mono getWorld(int id); Mono findAndUpdateWorld(int id, int randomNumber); diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/JdbcDbRepository.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/JdbcDbRepository.java deleted file mode 100644 index 5e159f816ed..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/JdbcDbRepository.java +++ /dev/null @@ -1,63 +0,0 @@ -package benchmark.repository; - -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.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Component; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Scheduler; - -@Component -@Profile("jdbc") -public class JdbcDbRepository implements DbRepository { - private final Logger log = LoggerFactory.getLogger(getClass()); - private final JdbcTemplate jdbcTemplate; - private final Scheduler scheduler; - - public JdbcDbRepository(JdbcTemplate jdbcTemplate, Scheduler scheduler) { - this.jdbcTemplate = jdbcTemplate; - this.scheduler = scheduler; - } - - @Override - public Mono getWorld(int id) { - log.debug("getWorld({})", id); - return Mono.fromCallable(() -> { - return jdbcTemplate.queryForObject( - "SELECT * FROM world WHERE id = ?", - (rs, rn) -> new World(rs.getInt("id"), rs.getInt("randomnumber")), - id); - }).subscribeOn(scheduler); - } - - private Mono updateWorld(World world) { - return Mono.fromCallable(() -> { - jdbcTemplate.update( - "UPDATE world SET randomnumber = ? WHERE id = ?", - world.randomnumber, - world.id); - return world; - }).subscribeOn(scheduler); - } - - @Override - public Mono findAndUpdateWorld(int id, int randomNumber) { - return getWorld(id).flatMap(world -> { - world.randomnumber = randomNumber; - return updateWorld(world); - }); - } - - @Override - public Flux fortunes() { - return Mono.fromCallable(() -> { - return jdbcTemplate.query( - "SELECT * FROM fortune", - (rs, rn) -> new Fortune(rs.getInt("id"), rs.getString("message"))); - }).subscribeOn(scheduler).flatMapIterable(fortunes -> 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 7e6b51ff39a..df777dd2d8e 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 @@ -5,7 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -18,22 +18,21 @@ @Component @Profile("mongo") public class MongoDbRepository implements DbRepository { - private final Logger log = LoggerFactory.getLogger(getClass()); - private final ReactiveMongoTemplate mongoTemplate; - public MongoDbRepository(ReactiveMongoTemplate mongoTemplate) { - this.mongoTemplate = mongoTemplate; + private final ReactiveMongoOperations operations; + + public MongoDbRepository(ReactiveMongoOperations operations) { + this.operations = operations; } @Override public Mono getWorld(int id) { - log.debug("getWorld({})", id); - return mongoTemplate.findById(id, World.class); + return operations.findById(id, World.class); } @Override public Mono findAndUpdateWorld(int id, int randomNumber) { - return mongoTemplate.findAndModify( + return operations.findAndModify( query(where("id").is(id)), update("randomNumber", randomNumber), options().returnNew(true), @@ -42,6 +41,6 @@ public Mono findAndUpdateWorld(int id, int randomNumber) { @Override public Flux fortunes() { - return mongoTemplate.findAll(Fortune.class); + return operations.findAll(Fortune.class); } } \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/PgClientDbRepository.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/PgClientDbRepository.java deleted file mode 100644 index 50dc6286ea1..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/PgClientDbRepository.java +++ /dev/null @@ -1,79 +0,0 @@ -package benchmark.repository; - -import benchmark.PgClients; -import benchmark.model.Fortune; -import benchmark.model.World; -import io.reactiverse.pgclient.PgIterator; -import io.reactiverse.pgclient.Row; -import io.reactiverse.pgclient.Tuple; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@Component -@Profile("pgclient") -public class PgClientDbRepository implements DbRepository { - private final Logger log = LoggerFactory.getLogger(getClass()); - private final PgClients pgClients; - - public PgClientDbRepository(PgClients pgClients) { - this.pgClients = pgClients; - } - - @Override - public Mono getWorld(int id) { - return Mono.create(sink -> - pgClients.getOne().preparedQuery("SELECT * FROM world WHERE id = $1", Tuple.of(id), ar -> { - if (ar.failed()) { - sink.error(ar.cause()); - } else { - - final Row row = ar.result().iterator().next(); - - World world = new World(row.getInteger(0), row.getInteger(1)); - sink.success(world); - } - })); - } - - private Mono updateWorld(World world) { - return Mono.create(sink -> { - pgClients.getOne().preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2", Tuple.of(world.randomnumber, world.id), ar -> { - if (ar.failed()) { - sink.error(ar.cause()); - } else { - sink.success(world); - } - }); - }); - } - - @Override - public Mono findAndUpdateWorld(int id, int randomNumber) { - return getWorld(id).flatMap(world -> { - world.randomnumber = randomNumber; - return updateWorld(world); - }); - } - - @Override - public Flux fortunes() { - return Flux.create(sink -> - pgClients.getOne().preparedQuery("SELECT * FROM fortune", ar -> { - if (ar.failed()) { - sink.error(ar.cause()); - return; - } - - PgIterator resultSet = ar.result().iterator(); - while (resultSet.hasNext()) { - Tuple row = resultSet.next(); - sink.next(new Fortune(row.getInteger(0), row.getString(1))); - } - sink.complete(); - })); - } -} \ No newline at end of file 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 a6979ffb058..c2524f07862 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,16 +1,18 @@ package benchmark.repository; -import benchmark.model.Fortune; -import benchmark.model.World; import org.springframework.context.annotation.Profile; -import org.springframework.data.r2dbc.core.DatabaseClient; +import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.stereotype.Component; + +import benchmark.model.Fortune; +import benchmark.model.World; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Component @Profile("r2dbc") public class R2dbcDbRepository implements DbRepository { + private final DatabaseClient databaseClient; public R2dbcDbRepository(DatabaseClient databaseClient) { @@ -19,17 +21,15 @@ public R2dbcDbRepository(DatabaseClient databaseClient) { @Override public Mono getWorld(int id) { - return databaseClient.execute() + return databaseClient .sql("SELECT id, randomnumber FROM world WHERE id = $1") .bind("$1", id) - .as(World.class) - .fetch() + .mapProperties(World.class) .first(); - } - public Mono updateWorld(World world) { - return databaseClient.execute() + private Mono updateWorld(World world) { + return databaseClient .sql("UPDATE world SET randomnumber=$2 WHERE id = $1") .bind("$1", world.id) .bind("$2", world.randomnumber) @@ -38,6 +38,8 @@ public Mono updateWorld(World world) { .map(count -> world); } + + @Override public Mono findAndUpdateWorld(int id, int randomNumber) { return getWorld(id).flatMap(world -> { world.randomnumber = randomNumber; @@ -47,10 +49,10 @@ public Mono findAndUpdateWorld(int id, int randomNumber) { @Override public Flux fortunes() { - return databaseClient.execute() + return databaseClient .sql("SELECT id, message FROM fortune") - .as(Fortune.class) - .fetch() + .mapProperties(Fortune.class) .all(); } + } \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/RxJdbcDbRepository.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/RxJdbcDbRepository.java deleted file mode 100644 index 1d07720f391..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/RxJdbcDbRepository.java +++ /dev/null @@ -1,60 +0,0 @@ -package benchmark.repository; - -import benchmark.model.Fortune; -import benchmark.model.World; -import io.reactivex.Flowable; -import org.davidmoten.rx.jdbc.Database; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@Component -@Profile("rxjdbc") -public class RxJdbcDbRepository implements DbRepository { - private final Database db; - - public RxJdbcDbRepository(Database db) { - this.db = db; - } - - @Override - public Mono getWorld(int id) { - String sql = "SELECT * FROM world WHERE id = ?"; - - Flowable worldFlowable = db.select(sql) - .parameters(id) - .get(rs -> { - World world = new World(rs.getInt("id"), rs.getInt("randomnumber")); - return world; - }); - - return Mono.from(worldFlowable); - } - - public Mono updateWorld(World world) { - String sql = "UPDATE world SET randomnumber = ? WHERE id = ?"; - - Flowable worldFlowable = db.update(sql) - .parameters(world.randomnumber, world.id) - .counts().map(cnt -> world); - return Mono.from(worldFlowable); - } - - public Mono findAndUpdateWorld(int id, int randomNumber) { - return getWorld(id).flatMap(world -> { - world.randomnumber = randomNumber; - return updateWorld(world); - }); - } - - @Override - public Flux fortunes() { - String sql = "SELECT * FROM fortune"; - - Flowable fortuneFlowable = db.select(sql) - .get(rs -> new Fortune(rs.getInt("id"), rs.getString("message"))); - - return Flux.from(fortuneFlowable); - } -} \ No newline at end of file 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 new file mode 100644 index 00000000000..e85d9c5be15 --- /dev/null +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/DbHandler.java @@ -0,0 +1,90 @@ +package benchmark.web; + +import java.util.List; + +import benchmark.Utils; +import benchmark.model.Fortune; +import benchmark.model.World; +import benchmark.repository.DbRepository; +import io.jstach.jstachio.JStachio; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class DbHandler { + + private static final String CONTENT_TYPE_VALUE = "text/html; charset=utf-8"; + + private final DbRepository dbRepository; + + public DbHandler(DbRepository dbRepository) { + this.dbRepository = dbRepository; + } + + public Mono db(ServerRequest request) { + int id = Utils.randomWorldNumber(); + Mono world = dbRepository.getWorld(id) + .switchIfEmpty(Mono.error(new Exception("No World found with Id: " + id))); + + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(world, World.class); + } + + public Mono queries(ServerRequest request) { + int queries = parseQueryCount(request.queryParams().getFirst("queries")); + + Mono> worlds = Flux.fromStream(Utils.randomWorldNumbers().limit(queries).boxed()) + .flatMap(dbRepository::getWorld) + .collectList(); + + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(worlds, new ParameterizedTypeReference>() { + }); + } + + private static int parseQueryCount(String maybeTextValue) { + if (maybeTextValue == null) { + return 1; + } + int parsedValue; + try { + parsedValue = Integer.parseInt(maybeTextValue); + } catch (NumberFormatException e) { + return 1; + } + return Math.min(500, Math.max(1, parsedValue)); + } + + 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(); + + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(worlds, new ParameterizedTypeReference>() { + }); + } + + public Mono fortunes(ServerRequest request) { + return dbRepository.fortunes() + .concatWith(Mono.just(new Fortune(0, "Additional fortune added at request time."))) + .collectSortedList() + .flatMap(fortunes -> + ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_VALUE) + .bodyValue(JStachio.render(new Fortunes(fortunes)))); + } + +} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/Fortunes.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/Fortunes.java new file mode 100644 index 00000000000..d8fc3dd7e2d --- /dev/null +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/Fortunes.java @@ -0,0 +1,10 @@ +package benchmark.web; + +import java.util.List; + +import benchmark.model.Fortune; +import io.jstach.jstache.JStache; + +@JStache(path = "fortunes.mustache") +public record Fortunes(List fortunes) { +} diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/JsonHandler.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/JsonHandler.java new file mode 100644 index 00000000000..6f4bc19249f --- /dev/null +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/JsonHandler.java @@ -0,0 +1,54 @@ +package benchmark.web; + +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import reactor.core.publisher.Mono; + +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.HandlerFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class JsonHandler implements HandlerFunction { + + private final ObjectWriter writer; + + public JsonHandler(ObjectMapper objectMapper) { + this.writer = objectMapper.writerFor(Map.class); + } + + @Override + public Mono handle(ServerRequest request) { + return ServerResponse.ok() + .body((message, context) -> { + DataBuffer buffer = serialize(request, Map.of("message", "Hello, world!")); + HttpHeaders headers = message.getHeaders(); + headers.setContentLength(buffer.readableByteCount()); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + return message.writeWith(Mono.just(buffer)); + }); + } + + private DataBuffer serialize(ServerRequest request, Object object) { + try { + byte[] bytes = this.writer.writeValueAsBytes(object); + return bufferFactory(request).wrap(bytes); + } + catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + + private static DataBufferFactory bufferFactory(ServerRequest request) { + return request.exchange().getResponse().bufferFactory(); + } + +} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/ServerFilter.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/ServerFilter.java similarity index 52% rename from frameworks/Java/spring-webflux/src/main/java/benchmark/ServerFilter.java rename to frameworks/Java/spring-webflux/src/main/java/benchmark/web/ServerFilter.java index 40cc32ff9a7..38357eefdb2 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/ServerFilter.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/ServerFilter.java @@ -1,20 +1,34 @@ -package benchmark; +package benchmark.web; -import io.netty.handler.codec.http.HttpHeaderNames; import org.springframework.http.HttpHeaders; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; +@Component public class ServerFilter implements WebFilter { + private static final String SERVER_NAME = "spring-webflux"; + private String date; + + public ServerFilter() { + updateDate(); + } + + @Scheduled(fixedRate = 1000) + public void updateDate() { + this.date = java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now()); + } + @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { HttpHeaders headers = exchange.getResponse().getHeaders(); - headers.add(HttpHeaderNames.SERVER.toString(), SERVER_NAME); - headers.add(HttpHeaderNames.DATE.toString(), java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now())); + headers.add(HttpHeaders.SERVER, SERVER_NAME); + headers.add(HttpHeaders.DATE, this.date); return chain.filter(exchange); } -} +} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/TextHandler.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/TextHandler.java new file mode 100644 index 00000000000..8abac92606d --- /dev/null +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/TextHandler.java @@ -0,0 +1,37 @@ +package benchmark.web; + +import java.nio.charset.StandardCharsets; + +import reactor.core.publisher.Mono; + +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.HandlerFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class TextHandler implements HandlerFunction { + + private static final byte[] TEXT_BODY = "Hello, World!".getBytes(StandardCharsets.UTF_8); + + private static final String TEXT_BODY_LENGTH = String.valueOf(TEXT_BODY.length); + + @Override + public Mono handle(ServerRequest request) { + return ServerResponse.ok() + .body((message, context) -> { + HttpHeaders headers = message.getHeaders(); + headers.add(HttpHeaders.CONTENT_LENGTH, TEXT_BODY_LENGTH); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE); + return message.writeWith(Mono.just(bufferFactory(request).wrap(TEXT_BODY))); + }); + } + + private static DataBufferFactory bufferFactory(ServerRequest request) { + return request.exchange().getResponse().bufferFactory(); + } + +} \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxHandler.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxHandler.java deleted file mode 100644 index e7fdeafa404..00000000000 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxHandler.java +++ /dev/null @@ -1,108 +0,0 @@ -package benchmark.web; - -import benchmark.model.Fortune; -import benchmark.model.World; -import benchmark.repository.DbRepository; - -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; - -import static java.util.Comparator.comparing; - -@Component -public class WebfluxHandler { - private final DbRepository dbRepository; - - public WebfluxHandler(DbRepository dbRepository) { - this.dbRepository = dbRepository; - } - - public Mono plaintext(ServerRequest request) { - return ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN) - .body(Mono.just("Hello, World!"), String.class); - } - - public Mono json(ServerRequest request) { - return ServerResponse.ok() - .contentType(MediaType.APPLICATION_JSON) - .body(Mono.just(Map.of("message", "Hello, World!")), Map.class); - } - - public Mono db(ServerRequest request) { - int id = randomWorldNumber(); - Mono world = dbRepository.getWorld(id) - .switchIfEmpty(Mono.error(new Exception("No World found with Id: " + id))); - - return ServerResponse.ok() - .contentType(MediaType.APPLICATION_JSON) - .body(world, World.class); - } - - public Mono queries(ServerRequest request) { - int queries = getQueries(request); - - Mono> worlds = Flux.range(0, queries) - .flatMap(i -> dbRepository.getWorld(randomWorldNumber())) - .collectList(); - - return ServerResponse.ok() - .contentType(MediaType.APPLICATION_JSON) - .body(worlds, new ParameterizedTypeReference>() { - }); - } - - private static int parseQueryCount(Optional maybeTextValue) { - if (!maybeTextValue.isPresent()) { - return 1; - } - int parsedValue; - try { - parsedValue = Integer.parseInt(maybeTextValue.get()); - } catch (NumberFormatException e) { - return 1; - } - return Math.min(500, Math.max(1, parsedValue)); - } - - public Mono updates(ServerRequest request) { - int queries = getQueries(request); - - Mono> worlds = Flux.range(0, queries) - .flatMap(i -> dbRepository.findAndUpdateWorld(randomWorldNumber(), randomWorldNumber())) - .collectList(); - - return ServerResponse.ok() - .contentType(MediaType.APPLICATION_JSON) - .body(worlds, new ParameterizedTypeReference>() { - }); - } - - public Mono fortunes(ServerRequest request) { - Mono> result = dbRepository.fortunes().collectList().flatMap(fortunes -> { - fortunes.add(new Fortune(0, "Additional fortune added at request time.")); - fortunes.sort(comparing(fortune -> fortune.message)); - return Mono.just(fortunes); - }); - - return ServerResponse.ok() - .render("fortunes", Collections.singletonMap("fortunes", result)); - } - - private static int getQueries(ServerRequest request) { - return parseQueryCount(request.queryParam("queries")); - } - - private static int randomWorldNumber() { - return 1 + ThreadLocalRandom.current().nextInt(10000); - } -} diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxRouter.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxRouter.java index b4dfb9be319..1499dc4f839 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxRouter.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxRouter.java @@ -1,37 +1,32 @@ package benchmark.web; +import reactor.core.publisher.Mono; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; -import static org.springframework.web.reactive.function.server.RequestPredicates.GET; - @Configuration public class WebfluxRouter { @Bean - public RouterFunction route(WebfluxHandler handler) { - return RouterFunctions - .route( - GET("/plaintext"), - handler::plaintext) - .andRoute( - GET("/json"), - handler::json) - .andRoute( - GET("/db"), - handler::db) - .andRoute( - GET("/queries"), - handler::queries) - .andRoute( - GET("/updates"), - handler::updates) - .andRoute( - GET("/fortunes"), - handler::fortunes) - ; + public RouterFunction route( + TextHandler textHandler, JsonHandler jsonHandler, DbHandler dbHandler) { + + return request -> { + HandlerFunction fn = switch (request.uri().getRawPath()) { + case "/plaintext" -> textHandler; + case "/json" -> jsonHandler; + case "/db" -> dbHandler::db; + case "/queries" -> dbHandler::queries; + case "/updates" -> dbHandler::updates; + case "/fortunes" -> dbHandler::fortunes; + default -> r -> ServerResponse.notFound().build(); + }; + return Mono.just(fn); + }; } + } \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/resources/application.yml b/frameworks/Java/spring-webflux/src/main/resources/application.yml index d45387cecde..45ca20c7d80 100755 --- a/frameworks/Java/spring-webflux/src/main/resources/application.yml +++ b/frameworks/Java/spring-webflux/src/main/resources/application.yml @@ -1,11 +1,3 @@ ---- -spring: - profiles: postgres - datasource: - url: jdbc:postgresql://${database.host}:${database.port}/${database.name} - username: ${database.username} - password: ${database.password} - database: name: hello_world host: tfb-database @@ -15,24 +7,27 @@ database: --- spring: - profiles: jdbc - ---- -spring: - profiles: pgclient - ---- -spring: - profiles: rxjdbc - ---- -spring: - profiles: r2dbc + config: + activate: + on-profile: r2dbc + autoconfigure: + exclude: org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration + r2dbc: + username: ${database.username} + password: ${database.password} + url: r2dbc:postgresql://${database.host}:${database.port}/${database.name}?loggerLevel=OFF&disableColumnSanitiser=true&assumeMinServerVersion=16&sslmode=disable + pool: + max-size: 256 --- spring: - profiles: mongo + config: + activate: + on-profile: mongo + autoconfigure: + exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration -database: - url: mongodb://tfb-database:27017/?waitQueueMultiple=200 - name: hello_world \ No newline at end of file + data: + mongodb: + uri: mongodb://tfb-database:27017/?waitQueueMultiple=200 + database: hello_world diff --git a/frameworks/Java/spring-webflux/src/main/resources/templates/fortunes.mustache b/frameworks/Java/spring-webflux/src/main/resources/fortunes.mustache similarity index 100% rename from frameworks/Java/spring-webflux/src/main/resources/templates/fortunes.mustache rename to frameworks/Java/spring-webflux/src/main/resources/fortunes.mustache diff --git a/frameworks/Java/spring/README.md b/frameworks/Java/spring/README.md index 8e25a585d44..742649fff7e 100644 --- a/frameworks/Java/spring/README.md +++ b/frameworks/Java/spring/README.md @@ -2,9 +2,7 @@ This is the Spring MVC portion of a [benchmarking test suite](../) comparing a variety of web development platforms. -An embedded undertow is used for the web server, with nearly everything configured with default settings. -The only thing changed is Hikari can use up to (2 * cores count) connections (the default is 10). -See [About-Pool-Sizing](https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing) +An embedded undertow is used for the web server. There are two implementations : * For postgresql access, JdbcTemplate is used. See [JdbcDbRepository](src/main/java/hello/JdbcDbRepository.java). diff --git a/frameworks/Java/spring/benchmark_config.json b/frameworks/Java/spring/benchmark_config.json index 615c4a478ca..72362984811 100644 --- a/frameworks/Java/spring/benchmark_config.json +++ b/frameworks/Java/spring/benchmark_config.json @@ -24,27 +24,6 @@ "notes": "", "versus": "" }, - "jpa": { - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "spring", - "language": "Java", - "flavor": "None", - "orm": "Full", - "platform": "Servlet", - "webserver": "Undertow", - "os": "Linux", - "database_os": "Linux", - "display_name": "spring-jpa", - "notes": "", - "versus": "spring" - }, "mongo": { "db_url": "/db", "query_url": "/queries?queries=", diff --git a/frameworks/Java/spring/pom.xml b/frameworks/Java/spring/pom.xml index b646277a3b0..6c9341fed54 100644 --- a/frameworks/Java/spring/pom.xml +++ b/frameworks/Java/spring/pom.xml @@ -11,12 +11,12 @@ org.springframework.boot spring-boot-starter-parent - 3.0.0 + 3.3.4 - 17 - 42.5.1 + 21 + 1.3.6 @@ -36,15 +36,23 @@ org.springframework.boot - spring-boot-starter-data-jpa + spring-boot-starter-jdbc org.springframework.boot spring-boot-starter-data-mongodb - org.springframework.boot - spring-boot-starter-mustache + io.jstach + jstachio + ${jstachio.version} + + + io.jstach + jstachio-apt + ${jstachio.version} + provided + true @@ -63,7 +71,13 @@ org.apache.maven.plugins maven-compiler-plugin - false + + + io.jstach + jstachio-apt + ${jstachio.version} + + diff --git a/frameworks/Java/spring/spring-jpa.dockerfile b/frameworks/Java/spring/spring-jpa.dockerfile deleted file mode 100644 index 4c197667a24..00000000000 --- a/frameworks/Java/spring/spring-jpa.dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM eclipse-temurin:17 as jre-build - -# Create a custom Java runtime -RUN $JAVA_HOME/bin/jlink \ - --add-modules ALL-MODULE-PATH \ - --strip-debug \ - --no-man-pages \ - --no-header-files \ - --compress=2 \ - --output /javaruntime - -FROM maven:3.8.5-openjdk-17-slim as maven -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH "${JAVA_HOME}/bin:${PATH}" -COPY --from=jre-build /javaruntime $JAVA_HOME - -RUN mvn -version -WORKDIR /spring -COPY src src -COPY pom.xml pom.xml -RUN mvn package -q - -FROM debian:bullseye-slim -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH "${JAVA_HOME}/bin:${PATH}" -COPY --from=jre-build /javaruntime $JAVA_HOME - -RUN java -version -WORKDIR /spring -COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar - -EXPOSE 8080 - -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseG1GC", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jpa"] diff --git a/frameworks/Java/spring/spring-mongo.dockerfile b/frameworks/Java/spring/spring-mongo.dockerfile index 3672481ca61..e700a21cabd 100644 --- a/frameworks/Java/spring/spring-mongo.dockerfile +++ b/frameworks/Java/spring/spring-mongo.dockerfile @@ -1,34 +1,15 @@ -FROM eclipse-temurin:17 as jre-build - -# Create a custom Java runtime -RUN $JAVA_HOME/bin/jlink \ - --add-modules ALL-MODULE-PATH \ - --strip-debug \ - --no-man-pages \ - --no-header-files \ - --compress=2 \ - --output /javaruntime - -FROM maven:3.8.5-openjdk-17-slim as maven -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH "${JAVA_HOME}/bin:${PATH}" -COPY --from=jre-build /javaruntime $JAVA_HOME - -RUN mvn -version +FROM maven:3.9.5-eclipse-temurin-21 as maven WORKDIR /spring COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM debian:bullseye-slim -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH "${JAVA_HOME}/bin:${PATH}" -COPY --from=jre-build /javaruntime $JAVA_HOME - -RUN java -version +FROM bellsoft/liberica-openjre-debian:21 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 +RUN java -Djarmode=tools -jar app.jar extract EXPOSE 8080 -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseG1GC", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=mongo"] +CMD ["java", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app/app.jar", "--spring.profiles.active=mongo"] \ No newline at end of file diff --git a/frameworks/Java/spring/spring.dockerfile b/frameworks/Java/spring/spring.dockerfile index a06133e8c45..f52c1f36df1 100644 --- a/frameworks/Java/spring/spring.dockerfile +++ b/frameworks/Java/spring/spring.dockerfile @@ -1,34 +1,15 @@ -FROM eclipse-temurin:17 as jre-build - -# Create a custom Java runtime -RUN $JAVA_HOME/bin/jlink \ - --add-modules ALL-MODULE-PATH \ - --strip-debug \ - --no-man-pages \ - --no-header-files \ - --compress=2 \ - --output /javaruntime - -FROM maven:3.8.5-openjdk-17-slim as maven -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH "${JAVA_HOME}/bin:${PATH}" -COPY --from=jre-build /javaruntime $JAVA_HOME - -RUN mvn -version +FROM maven:3.9.5-eclipse-temurin-21 as maven WORKDIR /spring COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM debian:bullseye-slim -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH "${JAVA_HOME}/bin:${PATH}" -COPY --from=jre-build /javaruntime $JAVA_HOME - -RUN java -version +FROM bellsoft/liberica-openjre-debian:21 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 +RUN java -Djarmode=tools -jar app.jar extract EXPOSE 8080 -CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseG1GC", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jdbc"] +CMD ["java", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app/app.jar", "--spring.profiles.active=jdbc"] \ No newline at end of file diff --git a/frameworks/Java/spring/src/main/java/hello/App.java b/frameworks/Java/spring/src/main/java/hello/App.java index e8e93b65902..da87b679f87 100644 --- a/frameworks/Java/spring/src/main/java/hello/App.java +++ b/frameworks/Java/spring/src/main/java/hello/App.java @@ -1,20 +1,11 @@ package hello; -import javax.sql.DataSource; - import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Profile; import org.springframework.context.event.EventListener; -import com.zaxxer.hikari.HikariDataSource; - -@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, MongoRepositoriesAutoConfiguration.class}) +@SpringBootApplication public class App { public static void main(String[] args) { @@ -22,17 +13,8 @@ public static void main(String[] args) { } @EventListener(ApplicationReadyEvent.class) - public void runAfterStartup() { - System.out.println("Application is ready"); - } - - @Bean - @Profile({ "jdbc", "jpa" }) - public DataSource datasource(DataSourceProperties dataSourceProperties) { - HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class) - .build(); - dataSource.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() * 2); - - return dataSource; + public void runAfterStartup() { + System.out.println("Application is ready"); } + } diff --git a/frameworks/Java/spring/src/main/java/hello/JpaConfig.java b/frameworks/Java/spring/src/main/java/hello/JpaConfig.java deleted file mode 100644 index c5b8576acab..00000000000 --- a/frameworks/Java/spring/src/main/java/hello/JpaConfig.java +++ /dev/null @@ -1,12 +0,0 @@ -package hello; - -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; - -@Profile("jpa") -@Configuration -@EnableJpaRepositories(basePackages = "hello.jpa") -public class JpaConfig { - -} diff --git a/frameworks/Java/spring/src/main/java/hello/Utils.java b/frameworks/Java/spring/src/main/java/hello/Utils.java new file mode 100644 index 00000000000..fbb1216624f --- /dev/null +++ b/frameworks/Java/spring/src/main/java/hello/Utils.java @@ -0,0 +1,19 @@ +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/spring/src/main/java/hello/controller/HelloController.java b/frameworks/Java/spring/src/main/java/hello/controller/HelloController.java deleted file mode 100644 index 6046a2e7426..00000000000 --- a/frameworks/Java/spring/src/main/java/hello/controller/HelloController.java +++ /dev/null @@ -1,128 +0,0 @@ -package hello.controller; - -import static java.util.Comparator.comparing; - -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; -import java.util.stream.IntStream; - -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import hello.model.Fortune; -import hello.model.World; -import hello.repository.DbRepository; - -@RestController -public final class HelloController { - - private DbRepository dbRepository; - - public HelloController(DbRepository dbRepository) { - this.dbRepository = dbRepository; - } - - @GetMapping(value = "/plaintext") - String plaintext(HttpServletResponse response) { - response.setContentType(MediaType.TEXT_PLAIN_VALUE); - return "Hello, World!"; - } - - @GetMapping("/json") - Message json(HttpServletResponse response) { - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - return new Message("Hello, World!"); - } - - @GetMapping("/db") - World db(HttpServletResponse response) { - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - return dbRepository.getWorld(randomWorldNumber()); - } - - @GetMapping("/queries") - World[] queries(HttpServletResponse response, @RequestParam(required = false) String queries) { - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - return randomWorldNumbers().mapToObj(dbRepository::getWorld).limit(parseQueryCount(queries)) - .toArray(World[]::new); - } - - @GetMapping("/updates") - World[] updates(HttpServletResponse response, @RequestParam(required = false) String queries) { - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - return randomWorldNumbers().mapToObj(dbRepository::getWorld).map(world -> { - // Ensure that the new random number is not equal to the old one. - // That would cause the JPA-based implementation to avoid sending the - // UPDATE query to the database, which would violate the test - // requirements. - - // Locally the records doesn't exist, maybe in the yours is ok but we need to - // make this check - if (world == null) { - return null; - } - - int newRandomNumber; - do { - newRandomNumber = randomWorldNumber(); - } while (newRandomNumber == world.randomnumber); - - return dbRepository.updateWorld(world, newRandomNumber); - }).limit(parseQueryCount(queries)).toArray(World[]::new); - } - - @GetMapping("/fortunes") - @ModelAttribute("fortunes") - List fortunes(HttpServletResponse response) { - response.setContentType(MediaType.TEXT_HTML_VALUE); - var fortunes = dbRepository.fortunes(); - - fortunes.add(new Fortune(0, "Additional fortune added at request time.")); - fortunes.sort(comparing(fortune -> fortune.message)); - return fortunes; - } - - private static final int MIN_WORLD_NUMBER = 1; - private static final int MAX_WORLD_NUMBER_PLUS_ONE = 10_001; - - private static int randomWorldNumber() { - return ThreadLocalRandom.current().nextInt(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE); - } - - private static IntStream randomWorldNumbers() { - return ThreadLocalRandom.current().ints(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE) - // distinct() allows us to avoid using Hibernate's first-level cache in - // the JPA-based implementation. Using a cache like that would bypass - // querying the database, which would violate the test requirements. - .distinct(); - } - - private static int parseQueryCount(String textValue) { - if (textValue == null) { - return 1; - } - int parsedValue; - try { - parsedValue = Integer.parseInt(textValue); - } catch (NumberFormatException e) { - return 1; - } - return Math.min(500, Math.max(1, parsedValue)); - } - - static class Message { - private final String message; - - public Message(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - } -} diff --git a/frameworks/Java/spring/src/main/java/hello/jpa/FortuneRepository.java b/frameworks/Java/spring/src/main/java/hello/jpa/FortuneRepository.java deleted file mode 100644 index 30dea98cc27..00000000000 --- a/frameworks/Java/spring/src/main/java/hello/jpa/FortuneRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package hello.jpa; - -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import hello.model.Fortune; - -@Repository -@Profile("jpa") -public interface FortuneRepository extends JpaRepository { -} diff --git a/frameworks/Java/spring/src/main/java/hello/jpa/JpaDbRepository.java b/frameworks/Java/spring/src/main/java/hello/jpa/JpaDbRepository.java deleted file mode 100644 index 2b58841a035..00000000000 --- a/frameworks/Java/spring/src/main/java/hello/jpa/JpaDbRepository.java +++ /dev/null @@ -1,38 +0,0 @@ -package hello.jpa; - -import java.util.List; - -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import hello.model.Fortune; -import hello.model.World; -import hello.repository.DbRepository; - -@Service -@Profile("jpa") -public class JpaDbRepository implements DbRepository { - private final WorldRepository worldRepository; - private final FortuneRepository fortuneRepository; - - public JpaDbRepository(WorldRepository worldRepository, FortuneRepository fortuneRepository) { - this.worldRepository = worldRepository; - this.fortuneRepository = fortuneRepository; - } - - @Override - public World getWorld(int id) { - return worldRepository.findById(id).orElse(null); - } - - @Override - public World updateWorld(World world, int randomNumber) { - world.randomnumber = randomNumber; - return worldRepository.save(world); - } - - @Override - public List fortunes() { - return fortuneRepository.findAll(); - } -} diff --git a/frameworks/Java/spring/src/main/java/hello/jpa/WorldRepository.java b/frameworks/Java/spring/src/main/java/hello/jpa/WorldRepository.java deleted file mode 100644 index 70361aa40d6..00000000000 --- a/frameworks/Java/spring/src/main/java/hello/jpa/WorldRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package hello.jpa; - -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import hello.model.World; - -@Repository -@Profile("jpa") -public interface WorldRepository extends JpaRepository { -} diff --git a/frameworks/Java/spring/src/main/java/hello/model/Fortune.java b/frameworks/Java/spring/src/main/java/hello/model/Fortune.java index e4ff559610a..a628d3c755f 100644 --- a/frameworks/Java/spring/src/main/java/hello/model/Fortune.java +++ b/frameworks/Java/spring/src/main/java/hello/model/Fortune.java @@ -1,33 +1,25 @@ package hello.model; -import jakarta.persistence.Entity; - import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; @Document -@Entity -public final class Fortune { +public final class Fortune implements Comparable{ + @Id - @jakarta.persistence.Id - public int id; - @Field("message") - public String message; + public final int id; - protected Fortune() { - } + @Field("message") + public final String message; public Fortune(int id, String message) { this.id = id; this.message = message; } - public int getId() { - return id; - } - - public String getMessage() { - return message; + @Override + public int compareTo(final Fortune other) { + return message.compareTo(other.message); } -} \ No newline at end of file +} diff --git a/frameworks/Java/spring/src/main/java/hello/model/World.java b/frameworks/Java/spring/src/main/java/hello/model/World.java index 2855df8a5d8..762e9e622ce 100644 --- a/frameworks/Java/spring/src/main/java/hello/model/World.java +++ b/frameworks/Java/spring/src/main/java/hello/model/World.java @@ -1,26 +1,22 @@ package hello.model; -import jakarta.persistence.Entity; - import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; @Document -@Entity public final class World { @Id - @jakarta.persistence.Id public int id; + @Field("randomNumber") - public int randomnumber; + public int randomNumber; - protected World() { - } - public World(int id, int randomnumber) { + public World(int id, int randomNumber) { this.id = id; - this.randomnumber = randomnumber; + this.randomNumber = randomNumber; } + } \ No newline at end of file diff --git a/frameworks/Java/spring/src/main/java/hello/repository/DbRepository.java b/frameworks/Java/spring/src/main/java/hello/repository/DbRepository.java index 5cfa8c7d5c3..d7733754c2c 100644 --- a/frameworks/Java/spring/src/main/java/hello/repository/DbRepository.java +++ b/frameworks/Java/spring/src/main/java/hello/repository/DbRepository.java @@ -6,9 +6,10 @@ import hello.model.World; public interface DbRepository { + World getWorld(int id); - World updateWorld(World world, int randomNumber); + void updateWorlds(List worlds); List fortunes(); } diff --git a/frameworks/Java/spring/src/main/java/hello/repository/JdbcDbRepository.java b/frameworks/Java/spring/src/main/java/hello/repository/JdbcDbRepository.java index f1dcdae0352..bc706e232c8 100644 --- a/frameworks/Java/spring/src/main/java/hello/repository/JdbcDbRepository.java +++ b/frameworks/Java/spring/src/main/java/hello/repository/JdbcDbRepository.java @@ -1,10 +1,15 @@ package hello.repository; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import org.springframework.context.annotation.Profile; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter; import org.springframework.stereotype.Repository; import hello.model.Fortune; @@ -22,27 +27,34 @@ public JdbcDbRepository(JdbcTemplate jdbcTemplate) { @Override public World getWorld(int id) { try { - return jdbcTemplate.queryForObject("SELECT * FROM world WHERE id = ?", - (rs, rn) -> new World(rs.getInt("id"), rs.getInt("randomnumber")), id); + return jdbcTemplate.queryForObject("SELECT id, randomnumber FROM world WHERE id = ?", + (rs, rn) -> new World(rs.getInt(1), rs.getInt(2)), id); } catch (EmptyResultDataAccessException e) { return null; } } - private World updateWorld(World world) { - jdbcTemplate.update("UPDATE world SET randomnumber = ? WHERE id = ?", world.randomnumber, world.id); - return world; - } - @Override - public World updateWorld(World world, int randomNumber) { - world.randomnumber = randomNumber; - return updateWorld(world); + public void updateWorlds(List worlds) { + jdbcTemplate.batchUpdate("UPDATE world SET randomnumber = ? WHERE id = ?", worlds, worlds.size(), new ParameterizedPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, World world) throws SQLException { + ps.setInt(1, world.randomNumber); + ps.setInt(2, world.id); + } + }); } @Override public List fortunes() { - return jdbcTemplate.query("SELECT * FROM fortune", - (rs, rn) -> new Fortune(rs.getInt("id"), rs.getString("message"))); + return jdbcTemplate.query(con -> con.prepareStatement("SELECT id, message FROM fortune", + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY), rs -> { + List results = new ArrayList<>(); + while (rs.next()) { + results.add(new Fortune(rs.getInt(1), rs.getString(2))); + } + return results; + }); } + } diff --git a/frameworks/Java/spring/src/main/java/hello/repository/MongoDbRepository.java b/frameworks/Java/spring/src/main/java/hello/repository/MongoDbRepository.java index 66c81e64f1d..9b6b67c4c95 100644 --- a/frameworks/Java/spring/src/main/java/hello/repository/MongoDbRepository.java +++ b/frameworks/Java/spring/src/main/java/hello/repository/MongoDbRepository.java @@ -1,11 +1,18 @@ package hello.repository; +import java.util.ArrayList; import java.util.List; import org.springframework.context.annotation.Profile; +import org.springframework.data.mongodb.core.BulkOperations; import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Repository; +import com.mongodb.bulk.BulkWriteResult; +import hello.Utils; import hello.model.Fortune; import hello.model.World; @@ -24,9 +31,14 @@ public World getWorld(int id) { } @Override - public World updateWorld(World world, int randomNumber) { - world.randomnumber = randomNumber; - return mongoTemplate.save(world); + public void updateWorlds(List worlds) { + BulkOperations bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, World.class); + for (World world : worlds) { + Query query = new Query().addCriteria(new Criteria("_id").is(world.id)); + Update update = new Update().set("randomNumber", world.randomNumber); + bulkOps.updateOne(query, update); + } + bulkOps.execute(); } @Override diff --git a/frameworks/Java/spring/src/main/java/hello/web/DbHandler.java b/frameworks/Java/spring/src/main/java/hello/web/DbHandler.java new file mode 100644 index 00000000000..983eb79f6b9 --- /dev/null +++ b/frameworks/Java/spring/src/main/java/hello/web/DbHandler.java @@ -0,0 +1,85 @@ +package hello.web; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import hello.Utils; +import hello.model.Fortune; +import hello.model.World; +import hello.repository.DbRepository; +import io.jstach.jstachio.JStachio; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.function.ServerRequest; +import org.springframework.web.servlet.function.ServerResponse; + +@Component +public class DbHandler { + + private final DbRepository dbRepository; + + public DbHandler(DbRepository dbRepository) { + this.dbRepository = dbRepository; + } + + ServerResponse db(ServerRequest request) { + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(dbRepository.getWorld(Utils.randomWorldNumber())); + } + + ServerResponse queries(ServerRequest request) { + int queries = parseQueryCount(request.params().getFirst("queries")); + World[] worlds = Utils.randomWorldNumbers() + .mapToObj(dbRepository::getWorld).limit(queries) + .toArray(World[]::new); + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(worlds); + } + + ServerResponse updates(ServerRequest request) { + int queries = parseQueryCount(request.params().getFirst("queries")); + 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); + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(worlds); + } + + ServerResponse fortunes(ServerRequest request) { + List fortunes = dbRepository.fortunes(); + fortunes.add(new Fortune(0, "Additional fortune added at request time.")); + Collections.sort(fortunes); + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML_VALUE) + .body(JStachio.render(new Fortunes(fortunes))); + } + + private static int parseQueryCount(String textValue) { + if (textValue == null) { + return 1; + } + int parsedValue; + try { + parsedValue = Integer.parseInt(textValue); + } catch (NumberFormatException e) { + return 1; + } + return Math.min(500, Math.max(1, parsedValue)); + } +} diff --git a/frameworks/Java/spring/src/main/java/hello/web/Fortunes.java b/frameworks/Java/spring/src/main/java/hello/web/Fortunes.java new file mode 100644 index 00000000000..cbd6daf2396 --- /dev/null +++ b/frameworks/Java/spring/src/main/java/hello/web/Fortunes.java @@ -0,0 +1,10 @@ +package hello.web; + +import java.util.List; + +import hello.model.Fortune; +import io.jstach.jstache.JStache; + +@JStache(path = "fortunes.mustache") +public record Fortunes(List fortunes) { +} diff --git a/frameworks/Java/spring/src/main/java/hello/web/JsonHandler.java b/frameworks/Java/spring/src/main/java/hello/web/JsonHandler.java new file mode 100644 index 00000000000..c6690811eca --- /dev/null +++ b/frameworks/Java/spring/src/main/java/hello/web/JsonHandler.java @@ -0,0 +1,39 @@ +package hello.web; + +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.function.HandlerFunction; +import org.springframework.web.servlet.function.ServerRequest; +import org.springframework.web.servlet.function.ServerResponse; + +@Component +public class JsonHandler implements HandlerFunction { + + private final ObjectWriter writer; + + public JsonHandler(ObjectMapper objectMapper) { + this.writer = objectMapper.writerFor(Map.class); + } + + @Override + public ServerResponse handle(ServerRequest request) { + try { + byte[] body = this.writer.writeValueAsBytes(Map.of("message", "Hello, world!")); + return ServerResponse.ok() + .contentLength(body.length) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(body); + } + catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/frameworks/Java/spring/src/main/java/hello/web/TextHandler.java b/frameworks/Java/spring/src/main/java/hello/web/TextHandler.java new file mode 100644 index 00000000000..b70b3010268 --- /dev/null +++ b/frameworks/Java/spring/src/main/java/hello/web/TextHandler.java @@ -0,0 +1,27 @@ +package hello.web; + +import java.nio.charset.StandardCharsets; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.function.HandlerFunction; +import org.springframework.web.servlet.function.ServerRequest; +import org.springframework.web.servlet.function.ServerResponse; + +@Component +public class TextHandler implements HandlerFunction { + + private static final byte[] TEXT_BODY = "Hello, World!".getBytes(StandardCharsets.UTF_8); + + private static final String TEXT_BODY_LENGTH = String.valueOf(TEXT_BODY.length); + + @Override + public ServerResponse handle(ServerRequest request) { + return ServerResponse.ok() + .header(HttpHeaders.CONTENT_LENGTH, TEXT_BODY_LENGTH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) + .body(TEXT_BODY); + } + +} diff --git a/frameworks/Java/spring/src/main/java/hello/web/WebmvcRouter.java b/frameworks/Java/spring/src/main/java/hello/web/WebmvcRouter.java new file mode 100644 index 00000000000..0a5810affd2 --- /dev/null +++ b/frameworks/Java/spring/src/main/java/hello/web/WebmvcRouter.java @@ -0,0 +1,32 @@ +package hello.web; + +import java.util.Optional; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.function.HandlerFunction; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.ServerResponse; + +@Configuration +public class WebmvcRouter { + + @Bean + public RouterFunction route( + TextHandler textHandler, + JsonHandler jsonHandler, + DbHandler dbHandler) { + + return request -> Optional.of((HandlerFunction) r -> + switch (r.uri().getRawPath()) { + case "/plaintext" -> textHandler.handle(r); + case "/json" -> jsonHandler.handle(r); + case "/db" -> dbHandler.db(r); + case "/queries" -> dbHandler.queries(r); + case "/updates" -> dbHandler.updates(r); + case "/fortunes" -> dbHandler.fortunes(r); + default -> ServerResponse.notFound().build(); + }); + } +} + diff --git a/frameworks/Java/spring/src/main/resources/application.yml b/frameworks/Java/spring/src/main/resources/application.yml index 0f411d6d8e9..efde83cda61 100644 --- a/frameworks/Java/spring/src/main/resources/application.yml +++ b/frameworks/Java/spring/src/main/resources/application.yml @@ -1,13 +1,21 @@ +server: + server-header: Spring + servlet: + encoding: + force: true --- spring: config: activate: - on-profile: jdbc,jpa + on-profile: jdbc + autoconfigure: + exclude: org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration datasource: - url: jdbc:postgresql://${database.host}:${database.port}/${database.name} + 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 database: name: hello_world host: tfb-database @@ -15,20 +23,13 @@ database: username: benchmarkdbuser password: benchmarkdbpass ---- -spring: - config: - activate: - on-profile: jpa - jpa: - database-platform: org.hibernate.dialect.PostgreSQLDialect - open-in-view: false - --- spring: config: activate: on-profile: mongo + autoconfigure: + exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration spring.data.mongodb: host: tfb-database @@ -39,6 +40,3 @@ spring.data.mongodb: spring: profiles: active: jdbc - -server.server-header: Spring -server.servlet.encoding.force: true \ No newline at end of file diff --git a/frameworks/Java/spring/src/main/resources/templates/fortunes.mustache b/frameworks/Java/spring/src/main/resources/fortunes.mustache similarity index 100% rename from frameworks/Java/spring/src/main/resources/templates/fortunes.mustache rename to frameworks/Java/spring/src/main/resources/fortunes.mustache diff --git a/frameworks/Java/undertow-jersey/pom.xml b/frameworks/Java/undertow-jersey/pom.xml index bf4603735ca..af553eea552 100644 --- a/frameworks/Java/undertow-jersey/pom.xml +++ b/frameworks/Java/undertow-jersey/pom.xml @@ -174,7 +174,7 @@ io.undertow undertow-core - 2.3.14.Final + 2.3.17.Final diff --git a/frameworks/Java/undertow/pom.xml b/frameworks/Java/undertow/pom.xml index 1a0f95afd69..133d1ce186c 100644 --- a/frameworks/Java/undertow/pom.xml +++ b/frameworks/Java/undertow/pom.xml @@ -20,7 +20,7 @@ 3.2.2 0.9.10 42.7.2 - 2.3.12.Final + 2.3.17.Final diff --git a/frameworks/Java/vertx/pom.xml b/frameworks/Java/vertx/pom.xml index 12ef914a5c5..4230d89326b 100644 --- a/frameworks/Java/vertx/pom.xml +++ b/frameworks/Java/vertx/pom.xml @@ -10,10 +10,10 @@ 17 vertx.App - 4.5.3 - 2.15.0 - 4.1.92.Final - 0.0.21.Final + 4.5.9 + 2.16.1 + 4.1.111.Final + 0.0.25.Final @@ -28,14 +28,14 @@ ${stack.version} - com.fasterxml.jackson.core - jackson-core - ${jackson.version} + com.dslplatform + dsl-json + 2.0.2 - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} + com.julienviet + jsonsergen + 0.0.5 io.netty @@ -75,6 +75,16 @@ false + + + default-compile + + + io.vertx.codegen.CodeGenProcessor + + + +
com.fizzed @@ -91,7 +101,7 @@ 17 ${basedir}/src/main/templates ${basedir}/target/generated-sources/rocker - false + true true true true @@ -138,9 +148,6 @@ Linux false - - unix - diff --git a/frameworks/Java/vertx/src/main/java/vertx/App.java b/frameworks/Java/vertx/src/main/java/vertx/App.java index 6d225d77d6c..5afa269ae9e 100755 --- a/frameworks/Java/vertx/src/main/java/vertx/App.java +++ b/frameworks/Java/vertx/src/main/java/vertx/App.java @@ -12,23 +12,12 @@ import io.vertx.core.http.HttpServerOptions; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.json.Json; -import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; -import io.vertx.sqlclient.PreparedQuery; -import io.vertx.sqlclient.PreparedStatement; -import io.vertx.sqlclient.Row; -import io.vertx.sqlclient.RowIterator; -import io.vertx.sqlclient.RowSet; -import io.vertx.sqlclient.Tuple; +import io.vertx.sqlclient.*; import io.vertx.sqlclient.impl.SqlClientInternal; -import vertx.model.CachedWorld; -import vertx.model.Fortune; -import vertx.model.Message; -import vertx.model.World; -import vertx.model.WorldCache; +import vertx.model.*; import vertx.rocker.BufferRockerOutput; import java.io.ByteArrayOutputStream; @@ -41,13 +30,16 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; +import java.util.stream.IntStream; public class App extends AbstractVerticle implements Handler { + private static final int NUM_PROCESSORS = Runtime.getRuntime().availableProcessors(); + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(App.class); + /** * Returns the value of the "queries" getRequest parameter, which is an integer * bound between 1 and 500 with a default value of 1. @@ -69,7 +61,9 @@ static int getQueries(HttpServerRequest request) { } } - static Logger logger = LoggerFactory.getLogger(App.class.getName()); + private static Logger logger = LoggerFactory.getLogger(App.class.getName()); + + private static final Integer[] BOXED_RND = IntStream.range(1, 10001).boxed().toArray(Integer[]::new); private static final String PATH_PLAINTEXT = "/plaintext"; private static final String PATH_JSON = "/json"; @@ -83,7 +77,7 @@ static int getQueries(HttpServerRequest request) { private static final CharSequence RESPONSE_TYPE_PLAIN = HttpHeaders.createOptimized("text/plain"); private static final CharSequence RESPONSE_TYPE_HTML = HttpHeaders.createOptimized("text/html; charset=UTF-8"); - private static final CharSequence RESPONSE_TYPE_JSON = HttpHeaders.createOptimized("application/json"); + static final CharSequence RESPONSE_TYPE_JSON = HttpHeaders.createOptimized("application/json"); private static final String HELLO_WORLD = "Hello, world!"; private static final Buffer HELLO_WORLD_BUFFER = Buffer.buffer(HELLO_WORLD, "UTF-8"); @@ -96,11 +90,27 @@ static int getQueries(HttpServerRequest request) { private static final CharSequence HELLO_WORLD_LENGTH = HttpHeaders.createOptimized("" + HELLO_WORLD.length()); private static final CharSequence SERVER = HttpHeaders.createOptimized("vert.x"); - private static final String UPDATE_WORLD = "UPDATE world SET randomnumber=$1 WHERE id=$2"; private static final String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1"; private static final String SELECT_FORTUNE = "SELECT id, message from FORTUNE"; private static final String SELECT_WORLDS = "SELECT id, randomnumber from WORLD"; + public static CharSequence createDateHeader() { + return HttpHeaders.createOptimized(DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now())); + } + + /** + * Returns a random integer that is a suitable value for both the {@code id} + * and {@code randomNumber} properties of a world object. + * + * @return a random world number + */ + static Integer boxedRandomWorldNumber() { + final int rndValue = ThreadLocalRandom.current().nextInt(1, 10001); + final var boxedRnd = BOXED_RND[rndValue - 1]; + assert boxedRnd.intValue() == rndValue; + return boxedRnd; + } + private HttpServer server; private SqlClientInternal client; private CharSequence dateString; @@ -110,14 +120,11 @@ static int getQueries(HttpServerRequest request) { private Throwable databaseErr; private PreparedQuery> SELECT_WORLD_QUERY; - private PreparedQuery> SELECT_FORTUNE_QUERY; - private PreparedQuery> UPDATE_WORLD_QUERY; + private PreparedQuery>> SELECT_FORTUNE_QUERY; + @SuppressWarnings("unchecked") + private PreparedQuery>[] AGGREGATED_UPDATE_WORLD_QUERY = new PreparedQuery[500]; private WorldCache WORLD_CACHE; - public static CharSequence createDateHeader() { - return HttpHeaders.createOptimized(DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now())); - } - @Override public void start(Promise startPromise) throws Exception { int port = 8080; @@ -138,32 +145,67 @@ public void start(Promise startPromise) throws Exception { options.setUser(config.getString("username", "benchmarkdbuser")); options.setPassword(config.getString("password", "benchmarkdbpass")); options.setCachePreparedStatements(true); + options.setPreparedStatementCacheMaxSize(1024); options.setPipeliningLimit(100_000); // Large pipelining means less flushing and we use a single connection anyway - PgConnection.connect(vertx, options) + Future clientsInit = initClients(options); + clientsInit + .transform(ar -> { + databaseErr = ar.cause(); + return server.listen(port); + }) + .mapEmpty() + .onComplete(startPromise); + } + + private Future initClients(PgConnectOptions options) { + return PgConnection.connect(vertx, options) .flatMap(conn -> { client = (SqlClientInternal) conn; + List> list = new ArrayList<>(); Future f1 = conn.prepare(SELECT_WORLD) .andThen(onSuccess(ps -> SELECT_WORLD_QUERY = ps.query())); + list.add(f1); Future f2 = conn.prepare(SELECT_FORTUNE) - .andThen(onSuccess(ps -> SELECT_FORTUNE_QUERY = ps.query())); - Future f3 = conn.prepare(UPDATE_WORLD) - .andThen(onSuccess(ps -> UPDATE_WORLD_QUERY = ps.query())); - Future f4 = conn.preparedQuery(SELECT_WORLDS) + .andThen(onSuccess(ps -> { + SELECT_FORTUNE_QUERY = ps.query(). + collecting(Collectors.mapping(row -> new Fortune(row.getInteger(0), row.getString(1)), Collectors.toList())); + })); + list.add(f2); + Future f3 = conn.preparedQuery(SELECT_WORLDS) .collecting(Collectors.mapping(row -> new CachedWorld(row.getInteger(0), row.getInteger(1)), Collectors.toList())) .execute() .map(worlds -> new WorldCache(worlds.value())) .andThen(onSuccess(wc -> WORLD_CACHE = wc)); - return CompositeFuture.join(f1, f2, f3, f4); - }) - .transform(ar -> { - databaseErr = ar.cause(); - return server.listen(port); - }) - .mapEmpty() - .onComplete(startPromise); + list.add(f3); + for (int i = 0; i < AGGREGATED_UPDATE_WORLD_QUERY.length; i++) { + int idx = i; + Future fut = conn + .prepare(buildAggregatedUpdateQuery(1 + idx)) + .andThen(onSuccess(ps -> AGGREGATED_UPDATE_WORLD_QUERY[idx] = ps.query())); + list.add(fut); + } + return Future.join(list); + }); + } + + private static String buildAggregatedUpdateQuery(int len) { + StringBuilder sql = new StringBuilder(); + sql.append("UPDATE WORLD SET RANDOMNUMBER = CASE ID"); + for (int i = 0; i < len; i++) { + int offset = (i * 2) + 1; + sql.append(" WHEN $").append(offset).append(" THEN $").append(offset + 1); + } + sql.append(" ELSE RANDOMNUMBER"); + sql.append(" END WHERE ID IN ($1"); + for (int i = 1; i < len; i++) { + int offset = (i * 2) + 1; + sql.append(",$").append(offset); + } + sql.append(")"); + return sql.toString(); } - private static Handler> onSuccess(Handler handler) { + public static Handler> onSuccess(Handler handler) { return ar -> { if (ar.succeeded()) { handler.handle(ar.result()); @@ -197,8 +239,9 @@ public void handle(HttpServerRequest request) { handleCaching(request); break; default: - request.response().setStatusCode(404); - request.response().end(); + request.response() + .setStatusCode(404) + .end(); break; } } catch (Exception e) { @@ -232,22 +275,12 @@ private void handleJson(HttpServerRequest request) { .add(HEADER_CONTENT_TYPE, RESPONSE_TYPE_JSON) .add(HEADER_SERVER, SERVER) .add(HEADER_DATE, dateString); - response.end(new Message("Hello, World!").toBuffer(), NULL_HANDLER); - } - - /** - * Returns a random integer that is a suitable value for both the {@code id} - * and {@code randomNumber} properties of a world object. - * - * @return a random world number - */ - private static int randomWorld() { - return 1 + ThreadLocalRandom.current().nextInt(10000); + response.end(new Message("Hello, World!").toJson(), NULL_HANDLER); } private void handleDb(HttpServerRequest req) { HttpServerResponse resp = req.response(); - SELECT_WORLD_QUERY.execute(Tuple.of(randomWorld()), res -> { + SELECT_WORLD_QUERY.execute(Tuple.of(boxedRandomWorldNumber()), res -> { if (res.succeeded()) { RowIterator resultSet = res.result().iterator(); if (!resultSet.hasNext()) { @@ -255,11 +288,12 @@ private void handleDb(HttpServerRequest req) { return; } Row row = resultSet.next(); - resp - .putHeader(HttpHeaders.SERVER, SERVER) - .putHeader(HttpHeaders.DATE, dateString) - .putHeader(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_JSON) - .end(Json.encode(new World(row.getInteger(0), row.getInteger(1))), NULL_HANDLER); + World word = new World(row.getInteger(0), row.getInteger(1)); + MultiMap headers = resp.headers(); + headers.add(HttpHeaders.SERVER, SERVER); + headers.add(HttpHeaders.DATE, dateString); + headers.add(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_JSON); + resp.end(word.toJson(), NULL_HANDLER); } else { sendError(req, res.cause()); } @@ -269,21 +303,26 @@ private void handleDb(HttpServerRequest req) { class Queries implements Handler>> { boolean failed; - JsonArray worlds = new JsonArray(); + final World[] worlds; final HttpServerRequest req; final HttpServerResponse resp; final int queries; + int worldsIndex; public Queries(HttpServerRequest req) { + int queries = getQueries(req); + this.req = req; this.resp = req.response(); - this.queries = getQueries(req); + this.queries = queries; + this.worlds = new World[queries]; + this.worldsIndex = 0; } private void handle() { client.group(c -> { for (int i = 0; i < queries; i++) { - c.preparedQuery(SELECT_WORLD).execute(Tuple.of(randomWorld()), this); + c.preparedQuery(SELECT_WORLD).execute(Tuple.of(boxedRandomWorldNumber()), this); } }); } @@ -299,50 +338,48 @@ public void handle(AsyncResult> ar) { // we need a final reference final Tuple row = ar.result().iterator().next(); - worlds.add(new JsonObject().put("id", "" + row.getInteger(0)).put("randomNumber", "" + row.getInteger(1))); + worlds[worldsIndex++] = new World(row.getInteger(0), row.getInteger(1)); // stop condition - if (worlds.size() == queries) { - resp - .putHeader(HttpHeaders.SERVER, SERVER) - .putHeader(HttpHeaders.DATE, dateString) - .putHeader(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_JSON) - .end(worlds.encode(), NULL_HANDLER); + if (worldsIndex == queries) { + MultiMap headers = resp.headers(); + headers.add(HttpHeaders.SERVER, SERVER); + headers.add(HttpHeaders.DATE, dateString); + headers.add(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_JSON); + resp.end(World.toJson(worlds), NULL_HANDLER); } } } } - class Update { + private class Update { - final HttpServerRequest req; - boolean failed; - int queryCount; - final World[] worlds; + private final HttpServerRequest request; + private final World[] worldsToUpdate; + private boolean failed; + private int selectWorldCompletedCount; - public Update(HttpServerRequest req) { - final int queries = getQueries(req); - this.req = req; - this.worlds = new World[queries]; + public Update(HttpServerRequest request) { + this.request = request; + this.worldsToUpdate = new World[getQueries(request)]; } - private void handle() { - + public void handle() { client.group(c -> { - PreparedQuery> preparedQuery = c.preparedQuery(SELECT_WORLD); - for (int i = 0; i < worlds.length; i++) { - int id = randomWorld(); - int index = i; - preparedQuery.execute(Tuple.of(id), ar2 -> { + final PreparedQuery> preparedQuery = c.preparedQuery(App.SELECT_WORLD); + for (int i = 0; i < worldsToUpdate.length; i++) { + final Integer id = boxedRandomWorldNumber(); + final int index = i; + preparedQuery.execute(Tuple.of(id), res -> { if (!failed) { - if (ar2.failed()) { + if (res.failed()) { failed = true; - sendError(req, ar2.cause()); + sendError(request, res.cause()); return; } - worlds[index] = new World(ar2.result().iterator().next().getInteger(0), randomWorld()); - if (++queryCount == worlds.length) { - handleUpdates(); + worldsToUpdate[index] = new World(res.result().iterator().next().getInteger(0), boxedRandomWorldNumber()); + if (++selectWorldCompletedCount == worldsToUpdate.length) { + randomWorldsQueryCompleted(); } } }); @@ -350,51 +387,52 @@ private void handle() { }); } - void handleUpdates() { - Arrays.sort(worlds); - List batch = new ArrayList<>(); - for (World world : worlds) { - batch.add(Tuple.of(world.getRandomNumber(), world.getId())); + private void randomWorldsQueryCompleted() { + Arrays.sort(worldsToUpdate); + final List params = new ArrayList<>(worldsToUpdate.length * 2); + for (int i = 0, count = worldsToUpdate.length;i < count;i++) { + var world = worldsToUpdate[i]; + params.add(world.getId()); + params.add(world.getRandomNumber()); } - UPDATE_WORLD_QUERY.executeBatch(batch, ar2 -> { - if (ar2.failed()) { - sendError(req, ar2.cause()); + AGGREGATED_UPDATE_WORLD_QUERY[worldsToUpdate.length - 1].execute(Tuple.wrap(params), updateResult -> { + if (updateResult.failed()) { + sendError(request, updateResult.cause()); return; } - JsonArray json = new JsonArray(); - for (World world : worlds) { - json.add(new JsonObject().put("id", "" + world.getId()).put("randomNumber", "" + world.getRandomNumber())); - } - req.response() - .putHeader(HttpHeaders.SERVER, SERVER) - .putHeader(HttpHeaders.DATE, dateString) - .putHeader(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_JSON) - .end(json.toBuffer(), NULL_HANDLER); + sendResponse(); }); } + + private void sendResponse() { + var res = request.response(); + MultiMap headers = res.headers(); + headers.add(HttpHeaders.SERVER, App.SERVER); + headers.add(HttpHeaders.DATE, dateString); + headers.add(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_JSON); + Buffer buff = WorldJsonSerializer.toJsonBuffer(worldsToUpdate); + res.end(buff, null); + } } private void handleFortunes(HttpServerRequest req) { SELECT_FORTUNE_QUERY.execute(ar -> { HttpServerResponse response = req.response(); if (ar.succeeded()) { - List fortunes = new ArrayList<>(); - RowIterator resultSet = ar.result().iterator(); - if (!resultSet.hasNext()) { + SqlResult> result = ar.result(); + if (result.size() == 0) { response.setStatusCode(404).end("No results"); return; } - while (resultSet.hasNext()) { - Row row = resultSet.next(); - fortunes.add(new Fortune(row.getInteger(0), row.getString(1))); - } + List fortunes = result.value(); fortunes.add(new Fortune(0, "Additional fortune added at request time.")); Collections.sort(fortunes); - response - .putHeader(HttpHeaders.SERVER, SERVER) - .putHeader(HttpHeaders.DATE, dateString) - .putHeader(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_HTML) - .end(FortunesTemplate.template(fortunes).render(factory).buffer(), NULL_HANDLER); + MultiMap headers = response.headers(); + headers.add(HttpHeaders.SERVER, SERVER); + headers.add(HttpHeaders.DATE, dateString); + headers.add(HttpHeaders.CONTENT_TYPE, RESPONSE_TYPE_HTML); + FortunesTemplate template = FortunesTemplate.template(fortunes); + response.end(template.render(factory).buffer(), NULL_HANDLER); } else { sendError(req, ar.cause()); } @@ -412,24 +450,18 @@ private void handleCaching(HttpServerRequest req) { } count = Math.max(1, count); count = Math.min(500, count); - CachedWorld[] worlds = WORLD_CACHE.getCachedWorld(count); - JsonArray json = new JsonArray(new ArrayList<>(count)); - for (int i = 0;i < count;i++) { - CachedWorld world = worlds[i]; - json.add(JsonObject.of("id", world.getId(), "randomNumber", world.getRandomNumber())); - } + List worlds = WORLD_CACHE.getCachedWorld(count); HttpServerResponse response = req.response(); MultiMap headers = response.headers(); headers .add(HEADER_CONTENT_TYPE, RESPONSE_TYPE_JSON) .add(HEADER_SERVER, SERVER) .add(HEADER_DATE, dateString); - response.end(json.toBuffer(), NULL_HANDLER); + response.end(CachedWorld.toJson(worlds), NULL_HANDLER); } public static void main(String[] args) throws Exception { - - int eventLoopPoolSize = VertxOptions.DEFAULT_EVENT_LOOP_POOL_SIZE; + int eventLoopPoolSize = NUM_PROCESSORS; String sizeProp = System.getProperty("vertx.eventLoopPoolSize"); if (sizeProp != null) { try { @@ -477,6 +509,7 @@ private static void printConfig(Vertx vertx) { logger.error("Could not read Vertx version", e);; } logger.info("Vertx: " + version); + logger.info("Processors: " + NUM_PROCESSORS); logger.info("Event Loop Size: " + ((MultithreadEventExecutorGroup)vertx.nettyEventLoopGroup()).executorCount()); logger.info("Native transport : " + nativeTransport); logger.info("Transport : " + transport); diff --git a/frameworks/Java/vertx/src/main/java/vertx/model/CachedWorld.java b/frameworks/Java/vertx/src/main/java/vertx/model/CachedWorld.java index b369cc824da..3a55bfc2e89 100644 --- a/frameworks/Java/vertx/src/main/java/vertx/model/CachedWorld.java +++ b/frameworks/Java/vertx/src/main/java/vertx/model/CachedWorld.java @@ -1,8 +1,17 @@ package vertx.model; +import com.julienviet.jsonsergen.Backend; +import com.julienviet.jsonsergen.JsonSerGen; +import io.vertx.codegen.annotations.DataObject; +import io.vertx.core.buffer.Buffer; + +import java.util.List; + /** * The model for the "world" database table. */ +@DataObject +@JsonSerGen(backends = Backend.DSL_JSON) public final class CachedWorld implements Comparable { private final int id; @@ -31,4 +40,8 @@ public int getRandomNumber() { public int compareTo(CachedWorld o) { return Integer.compare(id, o.id); } + + public static Buffer toJson(List worlds) { + return CachedWorldJsonSerializer.toJsonBuffer(worlds); + } } \ No newline at end of file diff --git a/frameworks/Java/vertx/src/main/java/vertx/model/Fortune.java b/frameworks/Java/vertx/src/main/java/vertx/model/Fortune.java index d1df6fcfa1a..eb21f93feee 100644 --- a/frameworks/Java/vertx/src/main/java/vertx/model/Fortune.java +++ b/frameworks/Java/vertx/src/main/java/vertx/model/Fortune.java @@ -7,11 +7,14 @@ /** * The model for the "fortune" database table. */ -public final class Fortune extends JsonObject implements Comparable { +public final class Fortune implements Comparable { private static final String ID = "id"; private static final String MESSAGE = "message"; + private final int id; + private final String message; + /** * Constructs a new fortune object with the given parameters. * @@ -19,20 +22,16 @@ public final class Fortune extends JsonObject implements Comparable { * @param message the message of the fortune */ public Fortune(int id, String message) { - put(ID, id); - put(MESSAGE, message); - } - - public Fortune(JsonObject doc) { - super(doc == null ? Collections.emptyMap() : doc.getMap()); + this.id = id; + this.message = message; } public int getId() { - return getInteger(ID); + return id; } public String getMessage() { - return getString(MESSAGE); + return message; } @Override diff --git a/frameworks/Java/vertx/src/main/java/vertx/model/Message.java b/frameworks/Java/vertx/src/main/java/vertx/model/Message.java index 0fe3274afe0..ab9cd2a3d48 100644 --- a/frameworks/Java/vertx/src/main/java/vertx/model/Message.java +++ b/frameworks/Java/vertx/src/main/java/vertx/model/Message.java @@ -1,16 +1,30 @@ package vertx.model; -import io.vertx.core.json.JsonObject; +import com.julienviet.jsonsergen.Backend; +import com.julienviet.jsonsergen.JsonSerGen; +import io.vertx.codegen.annotations.DataObject; +import io.vertx.core.buffer.Buffer; -public class Message extends JsonObject { +@DataObject +@JsonSerGen(backends = Backend.DSL_JSON) +public class Message { - private static final String MESSAGE = "message"; + private String message; public Message(String message) { - put(MESSAGE, message); + this.message = message; } public String getMessage() { - return getString(MESSAGE); + return message; + } + + public Message setMessage(String message) { + this.message = message; + return this; + } + + public Buffer toJson() { + return MessageJsonSerializer.toJsonBuffer(this); } } diff --git a/frameworks/Java/vertx/src/main/java/vertx/model/World.java b/frameworks/Java/vertx/src/main/java/vertx/model/World.java index e733b265d65..610f4371cab 100644 --- a/frameworks/Java/vertx/src/main/java/vertx/model/World.java +++ b/frameworks/Java/vertx/src/main/java/vertx/model/World.java @@ -1,8 +1,15 @@ package vertx.model; +import com.julienviet.jsonsergen.Backend; +import com.julienviet.jsonsergen.JsonSerGen; +import io.vertx.codegen.annotations.DataObject; +import io.vertx.core.buffer.Buffer; + /** * The model for the "world" database table. */ +@DataObject +@JsonSerGen(backends = Backend.DSL_JSON) public final class World implements Comparable { private final int id; @@ -31,4 +38,12 @@ public int getRandomNumber() { public int compareTo(World o) { return Integer.compare(id, o.id); } + + public Buffer toJson() { + return WorldJsonSerializer.toJsonBuffer(this); + } + + public static Buffer toJson(World[] worlds) { + return WorldJsonSerializer.toJsonBuffer(worlds); + } } \ No newline at end of file diff --git a/frameworks/Java/vertx/src/main/java/vertx/model/WorldCache.java b/frameworks/Java/vertx/src/main/java/vertx/model/WorldCache.java index c4d4b150283..2b0d4ae869c 100644 --- a/frameworks/Java/vertx/src/main/java/vertx/model/WorldCache.java +++ b/frameworks/Java/vertx/src/main/java/vertx/model/WorldCache.java @@ -3,6 +3,7 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -19,12 +20,12 @@ public WorldCache(List worlds) { this.cache = cache; } - public CachedWorld[] getCachedWorld(int count) { - CachedWorld[] ret = new CachedWorld[count]; + public List getCachedWorld(int count) { + List ret = new ArrayList<>(count); ThreadLocalRandom current = ThreadLocalRandom.current(); for (int i = 0;i < count;i++) { Integer key = Integer.valueOf(current.nextInt(1000)); - ret[i] = cache.getIfPresent(key); + ret.add(cache.getIfPresent(key)); } return ret; } diff --git a/frameworks/Java/vertx/src/main/java/vertx/model/package-info.java b/frameworks/Java/vertx/src/main/java/vertx/model/package-info.java new file mode 100644 index 00000000000..d0dfa9d492c --- /dev/null +++ b/frameworks/Java/vertx/src/main/java/vertx/model/package-info.java @@ -0,0 +1,4 @@ +@ModuleGen(name = "benchmark", groupPackage = "vertx.model") +package vertx.model; + +import io.vertx.codegen.annotations.ModuleGen; \ No newline at end of file diff --git a/frameworks/Java/vertx/src/main/templates/vertx/FortunesTemplate.rocker.html b/frameworks/Java/vertx/src/main/templates/vertx/FortunesTemplate.rocker.html index 4a76266c344..8ceb4d68c7e 100644 --- a/frameworks/Java/vertx/src/main/templates/vertx/FortunesTemplate.rocker.html +++ b/frameworks/Java/vertx/src/main/templates/vertx/FortunesTemplate.rocker.html @@ -1,18 +1,8 @@ @import vertx.model.Fortune @import java.util.List @args(List fortunes) - - -Fortunes - - - - - - @for ((ForIterator i, Fortune fortune) : fortunes) { - - - - }
idmessage
@fortune.getId()@fortune.getMessage()
- - +Fortunes +@for ((ForIterator i, Fortune fortune) : fortunes) { + +} +
idmessage
@fortune.getId()@fortune.getMessage()
\ No newline at end of file diff --git a/frameworks/Java/vertx/vertx-postgres.dockerfile b/frameworks/Java/vertx/vertx-postgres.dockerfile index 3072a3ad381..dec7fa545a3 100644 --- a/frameworks/Java/vertx/vertx-postgres.dockerfile +++ b/frameworks/Java/vertx/vertx-postgres.dockerfile @@ -14,6 +14,7 @@ CMD export DBIP=`getent hosts tfb-database | awk '{ print $1 }'` && \ -server \ -XX:+UseNUMA \ -XX:+UseParallelGC \ + -Djava.lang.Integer.IntegerCache.high=10000 \ -Dvertx.disableMetrics=true \ -Dvertx.disableH2c=true \ -Dvertx.disableWebsockets=true \ diff --git a/frameworks/Java/vertx/vertx.dockerfile b/frameworks/Java/vertx/vertx.dockerfile index cfd269e37ae..2fe2a27a2c2 100644 --- a/frameworks/Java/vertx/vertx.dockerfile +++ b/frameworks/Java/vertx/vertx.dockerfile @@ -6,4 +6,4 @@ RUN mvn package -q EXPOSE 8080 -CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dvertx.disableMetrics=true", "-Dvertx.disableH2c=true", "-Dvertx.disableWebsockets=true", "-Dvertx.flashPolicyHandler=false", "-Dvertx.threadChecks=false", "-Dvertx.disableContextTimings=true", "-Dvertx.disableTCCL=true", "-Dvertx.disableHttpHeadersValidation=true", "-Dio.netty.buffer.checkBounds=false", "-Dio.netty.buffer.checkAccessible=false", "-jar", "target/vertx.benchmark-0.0.1-SNAPSHOT-fat.jar", "src/main/conf/config.json"] +CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Djava.lang.Integer.IntegerCache.high=10000", "-Dvertx.disableMetrics=true", "-Dvertx.disableH2c=true", "-Dvertx.disableWebsockets=true", "-Dvertx.flashPolicyHandler=false", "-Dvertx.threadChecks=false", "-Dvertx.disableContextTimings=true", "-Dvertx.disableTCCL=true", "-Dvertx.disableHttpHeadersValidation=true", "-Dio.netty.buffer.checkBounds=false", "-Dio.netty.buffer.checkAccessible=false", "-jar", "target/vertx.benchmark-0.0.1-SNAPSHOT-fat.jar", "src/main/conf/config.json"] diff --git a/frameworks/Java/wicket/pom.xml b/frameworks/Java/wicket/pom.xml index ec30df52f27..c0f0303051d 100644 --- a/frameworks/Java/wicket/pom.xml +++ b/frameworks/Java/wicket/pom.xml @@ -24,7 +24,7 @@ 11 2.13.0 1.7.25 - 9.6.0 + 9.18.0 diff --git a/frameworks/Java/wildfly-ee/pom.xml b/frameworks/Java/wildfly-ee/pom.xml index c2e6472f6e3..bd77be766bb 100644 --- a/frameworks/Java/wildfly-ee/pom.xml +++ b/frameworks/Java/wildfly-ee/pom.xml @@ -13,27 +13,69 @@ 17 3.9.0 3.3.2 - 8.0 - 26.0.1.Final - 7.0.0.Final - 2.0.6.Final + 10.0.0 + 5.0.1.Final + 8.0.1.Final - + + + + jakarta.platform + jakarta.jakartaee-bom + ${version.jakarta.ee} + import + pom + + + + - javax - javaee-api - ${version.javaee.api} + jakarta.annotation + jakarta.annotation-api + provided + + + jakarta.enterprise + jakarta.enterprise.cdi-api provided - - org.glassfish.jaxb - jaxb-runtime - 2.4.0-b180830.0438 + jakarta.enterprise.concurrent + jakarta.enterprise.concurrent-api + provided + + + jakarta.inject + jakarta.inject-api + provided + + + jakarta.json + jakarta.json-api + provided + + + jakarta.persistence + jakarta.persistence-api + provided + + + jakarta.transaction + jakarta.transaction-api + provided + + + jakarta.validation + jakarta.validation-api + provided + + + jakarta.ws.rs + jakarta.ws.rs-api + provided - @@ -50,63 +92,50 @@ maven-compiler-plugin ${version.compiler.plugin} - ${java.version} - ${java.version} ${java.version}
+ + org.wildfly.plugins + wildfly-maven-plugin + ${version.wildfly-maven-plugin} + + ROOT.war + + + + org.wildfly + wildfly-ee-galleon-pack + + + org.wildfly + wildfly-datasources-galleon-pack + ${version.wildfly.galleon.datasources.feature.pack} + + + + + + org.wildfly.channels + wildfly-ee + + + + + jaxrs-server + jpa + jsf + mysql-driver + + + + + + + + + + - - - - bootable-jar - - false - - - - - org.wildfly.plugins - wildfly-jar-maven-plugin - ${version.wildfly.maven.jar.plugin} - - - - wildfly@maven(org.jboss.universe:community-universe)#${version.wildfly.bootable} - - - org.wildfly - wildfly-datasources-galleon-pack - ${version.wildfly.galleon.datasources.feature.pack} - - - - jaxrs-server - jsf - mysql-driver - - - deployment-scanner - - - - - - - - - - - - - package - - - - - - - - diff --git a/frameworks/Java/wildfly-ee/scripts/bootable-jar.cli b/frameworks/Java/wildfly-ee/scripts/wildfly-setup.cli similarity index 100% rename from frameworks/Java/wildfly-ee/scripts/bootable-jar.cli rename to frameworks/Java/wildfly-ee/scripts/wildfly-setup.cli diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/jpa/PersistenceResources.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/jpa/PersistenceResources.java similarity index 56% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/jpa/PersistenceResources.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/jpa/PersistenceResources.java index fcdc4aafa0d..aa580b31f68 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/jpa/PersistenceResources.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/jpa/PersistenceResources.java @@ -1,9 +1,9 @@ package com.techempower.ee7.jpa; -import javax.enterprise.context.Dependent; -import javax.enterprise.inject.Produces; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; public class PersistenceResources { diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/model/Fortune.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/model/Fortune.java similarity index 76% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/model/Fortune.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/model/Fortune.java index dce49416f75..7648c05b539 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/model/Fortune.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/model/Fortune.java @@ -2,13 +2,13 @@ import java.io.Serializable; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.NamedQuery; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQuery; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; @NamedQuery(name = "allFortunes", query = "SELECT f FROM Fortune f") @Entity diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/model/World.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/model/World.java similarity index 74% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/model/World.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/model/World.java index 44fd914aee7..1ee3ae0e97a 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/model/World.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/model/World.java @@ -2,11 +2,11 @@ import java.io.Serializable; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.validation.constraints.NotNull; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.validation.constraints.NotNull; @Entity public class World implements Serializable { diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/rest/CatchAllExceptionMapper.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/rest/CatchAllExceptionMapper.java similarity index 62% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/rest/CatchAllExceptionMapper.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/rest/CatchAllExceptionMapper.java index 0beb6e85c3d..bc287fda07a 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/rest/CatchAllExceptionMapper.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/rest/CatchAllExceptionMapper.java @@ -1,9 +1,9 @@ package com.techempower.ee7.rest; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; @Provider public class CatchAllExceptionMapper implements ExceptionMapper { diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/rest/MyApplication.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/rest/MyApplication.java similarity index 59% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/rest/MyApplication.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/rest/MyApplication.java index 5c82e49f3b9..c1f912ec17d 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/rest/MyApplication.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/rest/MyApplication.java @@ -1,7 +1,7 @@ package com.techempower.ee7.rest; -import javax.ws.rs.ApplicationPath; -import javax.ws.rs.core.Application; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; @ApplicationPath("rest") public class MyApplication extends Application { diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/Fortunes.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/Fortunes.java similarity index 79% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/Fortunes.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/Fortunes.java index 6bbff0fead8..8bd06ae545c 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/Fortunes.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/Fortunes.java @@ -3,11 +3,11 @@ import java.util.Collections; import java.util.List; -import javax.annotation.PostConstruct; -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; -import javax.inject.Named; -import javax.persistence.EntityManager; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.persistence.EntityManager; import com.techempower.ee7.model.Fortune; diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/JsonSerialization.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/JsonSerialization.java similarity index 81% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/JsonSerialization.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/JsonSerialization.java index ad5d947b042..019afc50fc7 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/JsonSerialization.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/JsonSerialization.java @@ -1,9 +1,9 @@ package com.techempower.ee7.tests; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("/json") public class JsonSerialization { diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/MultipleQueries.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/MultipleQueries.java similarity index 78% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/MultipleQueries.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/MultipleQueries.java index 936ddfb4b04..fbd764af7ee 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/MultipleQueries.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/MultipleQueries.java @@ -3,13 +3,13 @@ import java.util.ArrayList; import java.util.List; -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; import com.techempower.ee7.model.World; import com.techempower.ee7.util.Helpers; diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/PlainText.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/PlainText.java similarity index 78% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/PlainText.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/PlainText.java index f40da2ab7da..7ce9f37f3b9 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/PlainText.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/PlainText.java @@ -1,9 +1,9 @@ package com.techempower.ee7.tests; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("/plaintext") public class PlainText { diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/SingleQuery.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/SingleQuery.java similarity index 64% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/SingleQuery.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/SingleQuery.java index 30279ae4295..517ad710f45 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/SingleQuery.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/SingleQuery.java @@ -1,11 +1,11 @@ package com.techempower.ee7.tests; -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import com.techempower.ee7.model.World; import com.techempower.ee7.util.Helpers; diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/TestActions.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/TestActions.java similarity index 73% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/TestActions.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/TestActions.java index 9302373516c..d3847c9b661 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/TestActions.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/TestActions.java @@ -1,9 +1,9 @@ package com.techempower.ee7.tests; -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.transaction.Transactional; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; import com.techempower.ee7.model.World; import com.techempower.ee7.util.Helpers; diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/Updates.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/Updates.java similarity index 81% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/Updates.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/Updates.java index b041f24c939..5c3879bc363 100644 --- a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/tests/Updates.java +++ b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/tests/Updates.java @@ -3,12 +3,12 @@ import java.util.ArrayList; import java.util.List; -import javax.inject.Inject; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; import com.techempower.ee7.model.World; import com.techempower.ee7.util.Helpers; diff --git a/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/util/Helpers.java b/frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/util/Helpers.java similarity index 100% rename from frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee7/util/Helpers.java rename to frameworks/Java/wildfly-ee/src/main/java/com/techempower/ee/util/Helpers.java diff --git a/frameworks/Java/wildfly-ee/src/main/resources/META-INF/persistence.xml b/frameworks/Java/wildfly-ee/src/main/resources/META-INF/persistence.xml index 2ac545e5dde..ff728916fe5 100644 --- a/frameworks/Java/wildfly-ee/src/main/resources/META-INF/persistence.xml +++ b/frameworks/Java/wildfly-ee/src/main/resources/META-INF/persistence.xml @@ -6,7 +6,6 @@ NONE - diff --git a/frameworks/Java/wildfly-ee/src/main/webapp/WEB-INF/web.xml b/frameworks/Java/wildfly-ee/src/main/webapp/WEB-INF/web.xml index 96378e310c9..126c5588694 100644 --- a/frameworks/Java/wildfly-ee/src/main/webapp/WEB-INF/web.xml +++ b/frameworks/Java/wildfly-ee/src/main/webapp/WEB-INF/web.xml @@ -8,7 +8,7 @@ Faces Servlet - javax.faces.webapp.FacesServlet + jakarta.faces.webapp.FacesServlet 1 @@ -18,12 +18,12 @@ - javax.faces.STATE_SAVING_METHOD + jakarta.faces.STATE_SAVING_METHOD client - javax.faces.PROJECT_STAGE + jakarta.faces.PROJECT_STAGE Production diff --git a/frameworks/Java/wildfly-ee/wildfly-ee.dockerfile b/frameworks/Java/wildfly-ee/wildfly-ee.dockerfile index ec4a1c6e063..b04e261e64d 100644 --- a/frameworks/Java/wildfly-ee/wildfly-ee.dockerfile +++ b/frameworks/Java/wildfly-ee/wildfly-ee.dockerfile @@ -1,9 +1,8 @@ FROM maven:3-openjdk-17 WORKDIR /wildfly EXPOSE 8080 -ENV MAVEN_OPTS="--add-exports=java.xml/com.sun.org.apache.xerces.internal.parsers=ALL-UNNAMED --add-exports=java.xml/com.sun.org.apache.xerces.internal.util=ALL-UNNAMED" COPY src src COPY scripts scripts COPY pom.xml pom.xml -RUN mvn clean package -P bootable-jar -CMD java -Djava.net.preferIPv4Stack=true -XX:SoftMaxHeapSize=18g -Xmx24g -XX:+UseZGC -jar target/wildfly-ee-bootable.jar +RUN mvn clean package wildfly:package +CMD JAVA_OPTS="-Djava.net.preferIPv4Stack=true -XX:SoftMaxHeapSize=18g -Xmx24g -XX:+UseZGC" ./target/server/bin/standalone.sh diff --git a/frameworks/JavaScript/express/package.json b/frameworks/JavaScript/express/package.json index 32ea58ae528..09723c74dd8 100644 --- a/frameworks/JavaScript/express/package.json +++ b/frameworks/JavaScript/express/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "dependencies": { - "body-parser": "1.19.0", + "body-parser": "1.20.3", "dateformat": "3.0.3", "escape-html": "1.0.3", "express": "4.18.2", diff --git a/frameworks/JavaScript/hapi/package.json b/frameworks/JavaScript/hapi/package.json index 5d6070a266a..6a6c23a897b 100644 --- a/frameworks/JavaScript/hapi/package.json +++ b/frameworks/JavaScript/hapi/package.json @@ -10,7 +10,7 @@ "handlebars": "4.3.0", "mongoose": "5.13.20", "mysql": "2.16.0", - "mysql2": "3.9.7", + "mysql2": "3.9.8", "pg": "8.5.1", "pg-hstore": "2.3.2", "sequelize": "6.29.0" diff --git a/frameworks/JavaScript/nodejs/handlers/mysql-raw.js b/frameworks/JavaScript/nodejs/handlers/mysql-raw.js index 5fa1ea3c625..6063b4020e4 100644 --- a/frameworks/JavaScript/nodejs/handlers/mysql-raw.js +++ b/frameworks/JavaScript/nodejs/handlers/mysql-raw.js @@ -1,6 +1,6 @@ const h = require('../helper'); const async = require('async'); -const mysql = require('mysql'); +const mysql = require('mysql2'); const connection = mysql.createConnection({ host: 'tfb-database', user: 'benchmarkdbuser', diff --git a/frameworks/JavaScript/nodejs/package.json b/frameworks/JavaScript/nodejs/package.json index 8af5fedae2b..28dc35a011c 100644 --- a/frameworks/JavaScript/nodejs/package.json +++ b/frameworks/JavaScript/nodejs/package.json @@ -8,7 +8,7 @@ "mongodb": "3.7.3", "mongoose": "5.13.20", "mysql": "2.16.0", - "mysql2": "3.9.7", + "mysql2": "3.9.8", "parseurl": "1.3.2", "pg": "8.5.0", "pg-hstore": "2.3.2", diff --git a/frameworks/JavaScript/sailsjs/package.json b/frameworks/JavaScript/sailsjs/package.json index 9be38df56ad..b2886f07639 100644 --- a/frameworks/JavaScript/sailsjs/package.json +++ b/frameworks/JavaScript/sailsjs/package.json @@ -10,7 +10,7 @@ "ejs": "3.1.10", "handlebars": "4.7.6", "mysql": "2.16.0", - "mysql2": "3.9.7", + "mysql2": "3.9.8", "pg": "6.0.5", "pg-hstore": "2.3.2", "rc": "1.1.6", diff --git a/frameworks/JavaScript/spliffy/package-lock.json b/frameworks/JavaScript/spliffy/package-lock.json index db22f966f30..114cddc45d1 100644 --- a/frameworks/JavaScript/spliffy/package-lock.json +++ b/frameworks/JavaScript/spliffy/package-lock.json @@ -4,986 +4,2619 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "optional": true, - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" + "@aws-sdk/client-sso-oidc": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.621.0.tgz", + "integrity": "sha512-mMjk3mFUwV2Y68POf1BQMTF+F6qxt5tPu6daEUCNGC9Cenk3h2YXQQoS4/eSyYzuBiYk3vx49VgleRvdvkg8rg==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "requires": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } + }, + "@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "requires": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "requires": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } + }, + "@aws-sdk/client-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.621.0.tgz", + "integrity": "sha512-xpKfikN4u0BaUYZA9FGUMkkDmfoIP0Q03+A86WjqDWhcOoqNA1DkHsE4kZ+r064ifkPUfcNuUvlkVTEoBZoFjA==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.621.0.tgz", + "integrity": "sha512-0EWVnSc+JQn5HLnF5Xv405M8n4zfdx9gyGdpnCmAmFqEDHA8LmBdxJdpUk1Ovp/I5oPANhjojxabIW5f1uU0RA==", + "requires": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.621.0.tgz", + "integrity": "sha512-4JqpccUgz5Snanpt2+53hbOBbJQrSFq7E1sAAbgY6BKVQUsW5qyXqnjvSF32kDeKa5JpBl3bBWLZl04IadcPHw==", + "requires": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-ini": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.621.0.tgz", + "integrity": "sha512-Kza0jcFeA/GEL6xJlzR2KFf1PfZKMFnxfGzJzl5yN7EjoGdMijl34KaRyVnfRjnCWcsUpBWKNIDk9WZVMY9yiw==", + "requires": { + "@aws-sdk/client-sso": "3.621.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz", + "integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/token-providers": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", + "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/config-resolver": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", + "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "requires": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/credential-provider-imds": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "requires": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "requires": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/hash-node": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", + "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "requires": { + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "@smithy/invalid-dependency": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", + "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-content-length": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "requires": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "requires": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-retry": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.13.tgz", + "integrity": "sha512-zvCLfaRYCaUmjbF2yxShGZdolSHft7NNCTA28HVN9hKcEbOH+g5irr1X9s+in8EpambclGnevZY4A3lYpvDCFw==", + "requires": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + } + }, + "@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "requires": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "requires": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/service-error-classification": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", + "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "requires": { + "@smithy/types": "^3.3.0" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/smithy-client": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.11.tgz", + "integrity": "sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "requires": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "requires": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-browser": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.13.tgz", + "integrity": "sha512-ZIRSUsnnMRStOP6OKtW+gCSiVFkwnfQF2xtf32QKAbHR6ACjhbAybDvry+3L5qQYdh3H6+7yD/AiUE45n8mTTw==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-node": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.13.tgz", + "integrity": "sha512-voUa8TFJGfD+U12tlNNLCDlXibt9vRdNzRX45Onk/WxZe7TS+hTOZouEZRa7oARGicdgeXvt1A0W45qLGYdy+g==", + "requires": { + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-retry": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", + "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "requires": { + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "requires": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" } } }, - "@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "optional": true, - "requires": { - "tslib": "^1.11.1" + "@aws-sdk/core": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.621.0.tgz", + "integrity": "sha512-CtOwWmDdEiINkGXD93iGfXjN0WmCp9l45cDWHHGa8lRgEDyhuL7bwd/pH5aSzj0j8SiQBG2k0S7DHbd5RaqvbQ==", + "requires": { + "@smithy/core": "^2.3.1", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" }, "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "requires": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "requires": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "requires": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "requires": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/signature-v4": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/smithy-client": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.11.tgz", + "integrity": "sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "requires": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "requires": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "requires": { + "strnum": "^1.0.5" + } } } }, - "@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "optional": true, - "requires": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "@aws-sdk/credential-provider-http": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.621.0.tgz", + "integrity": "sha512-/jc2tEsdkT1QQAI5Dvoci50DbSxtJrevemwFsm0B73pwCcOQZ5ZwwSdVqGsPutzYzUVx3bcXg3LRL7jLACqRIg==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" }, "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "optional": true, - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "optional": true, - "requires": { - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "optional": true, - "requires": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "requires": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "requires": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "requires": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "requires": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/smithy-client": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.11.tgz", + "integrity": "sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "requires": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "requires": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + } } } }, - "@aws-sdk/client-cognito-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.398.0.tgz", - "integrity": "sha512-Pr/S1f8R2FsJ8DwBC6g0CSdtZNNV5dMHhlIi+t8YAmCJvP4KT+UhzFjbvQRINlBRLFuGUuP7p5vRcGVELD3+wA==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.398.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sso": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.398.0.tgz", - "integrity": "sha512-CygL0jhfibw4kmWXG/3sfZMFNjcXo66XUuPC4BqZBk8Rj5vFoxp1vZeMkDLzTIk97Nvo5J5Bh+QnXKhub6AckQ==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sts": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.398.0.tgz", - "integrity": "sha512-/3Pa9wLMvBZipKraq3AtbmTfXW6q9kyvhwOno64f1Fz7kFb8ijQFMGoATS70B2pGEZTlxkUqJFWDiisT6Q6dFg==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-sdk-sts": "3.398.0", - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.398.0.tgz", - "integrity": "sha512-MFUhy1YayHg5ypRTk4OTfDumQRP+OJBagaGv14kA8DzhKH1sNrU4HV7A7y2J4SvkN5hG/KnLJqxpakCtB2/O2g==", - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.398.0.tgz", - "integrity": "sha512-Z8Yj5z7FroAsR6UVML+XUdlpoqEe9Dnle8c2h8/xWwIC2feTfIBhjLhRVxfbpbM1pLgBSNEcZ7U8fwq5l7ESVQ==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.398.0.tgz", - "integrity": "sha512-AsK1lStK3nB9Cn6S6ODb1ktGh7SRejsNVQVKX3t5d3tgOaX+aX1Iwy8FzM/ZEN8uCloeRifUGIY9uQFygg5mSw==", - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.398.0.tgz", - "integrity": "sha512-odmI/DSKfuWUYeDnGTCEHBbC8/MwnF6yEq874zl6+owoVv0ZsYP8qBHfiJkYqrwg7wQ7Pi40sSAPC1rhesGwzg==", - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-ini": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.398.0.tgz", - "integrity": "sha512-WrkBL1W7TXN508PA9wRXPFtzmGpVSW98gDaHEaa8GolAPHMPa5t2QcC/z/cFpglzrcVv8SA277zu9Z8tELdZhg==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.398.0.tgz", - "integrity": "sha512-2Dl35587xbnzR/GGZqA2MnFs8+kS4wbHQO9BioU0okA+8NRueohNMdrdQmQDdSNK4BfIpFspiZmFkXFNyEAfgw==", - "optional": true, - "requires": { - "@aws-sdk/client-sso": "3.398.0", - "@aws-sdk/token-providers": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.398.0.tgz", - "integrity": "sha512-iG3905Alv9pINbQ8/MIsshgqYMbWx+NDQWpxbIW3W0MkSH3iAqdVpSCteYidYX9G/jv2Um1nW3y360ib20bvNg==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, "@aws-sdk/credential-providers": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.398.0.tgz", - "integrity": "sha512-355vXmImn2e85mIWSYDVb101AF2lIVHKNCaH6sV1U/8i0ZOXh2cJYNdkRYrxNt1ezDB0k97lSKvuDx7RDvJyRg==", - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.398.0", - "@aws-sdk/client-sso": "3.398.0", - "@aws-sdk/client-sts": "3.398.0", - "@aws-sdk/credential-provider-cognito-identity": "3.398.0", - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-ini": "3.398.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.398.0.tgz", - "integrity": "sha512-m+5laWdBaxIZK2ko0OwcCHJZJ5V1MgEIt8QVQ3k4/kOkN9ICjevOYmba751pHoTnbOYB7zQd6D2OT3EYEEsUcA==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.398.0.tgz", - "integrity": "sha512-CiJjW+FL12elS6Pn7/UVjVK8HWHhXMfvHZvOwx/Qkpy340sIhkuzOO6fZEruECDTZhl2Wqn81XdJ1ZQ4pRKpCg==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.398.0.tgz", - "integrity": "sha512-7QpOqPQAZNXDXv6vsRex4R8dLniL0E/80OPK4PPFsrCh9btEyhN9Begh4i1T+5lL28hmYkztLOkTQ2N5J3hgRQ==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-sdk-sts": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.398.0.tgz", - "integrity": "sha512-+JH76XHEgfVihkY+GurohOQ5Z83zVN1nYcQzwCFnCDTh4dG4KwhnZKG+WPw6XJECocY0R+H0ivofeALHvVWJtQ==", - "optional": true, - "requires": { - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.398.0.tgz", - "integrity": "sha512-O0KqXAix1TcvZBFt1qoFkHMUNJOSgjJTYS7lFTRKSwgsD27bdW2TM2r9R8DAccWFt5Amjkdt+eOwQMIXPGTm8w==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.398.0.tgz", - "integrity": "sha512-nF1jg0L+18b5HvTcYzwyFgfZQQMELJINFqI0mi4yRKaX7T5a3aGp5RVLGGju/6tAGTuFbfBoEhkhU3kkxexPYQ==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/token-providers": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.398.0.tgz", - "integrity": "sha512-nrYgjzavGCKJL/48Vt0EL+OlIc5UZLfNGpgyUW9cv3XZwl+kXV0QB+HH0rHZZLfpbBgZ2RBIJR9uD5ieu/6hpQ==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/types": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", - "integrity": "sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.398.0.tgz", - "integrity": "sha512-Fy0gLYAei/Rd6BrXG4baspCnWTUSd0NdokU1pZh4KlfEAEN1i8SPPgfiO5hLk7+2inqtCmqxVJlfqbMVe9k4bw==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-locate-window": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", - "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.398.0.tgz", - "integrity": "sha512-A3Tzx1tkDHlBT+IgxmsMCHbV8LM7SwwCozq2ZjJRx0nqw3MCrrcxQFXldHeX/gdUMO+0Oocb7HGSnVODTq+0EA==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.398.0.tgz", - "integrity": "sha512-RTVQofdj961ej4//fEkppFf4KXqKGMTCqJYghx3G0C/MYXbg7MGl7LjfNGtJcboRE8pfHHQ/TUWBDA7RIAPPlQ==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "optional": true, - "requires": { - "tslib": "^2.3.1" - } - }, - "@mongodb-js/saslprep": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", - "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", - "optional": true, - "requires": { - "sparse-bitfield": "^3.0.3" - } - }, - "@smithy/abort-controller": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.5.tgz", - "integrity": "sha512-byVZ2KWLMPYAZGKjRpniAzLcygJO4ruClZKdJTuB0eCB76ONFTdptBHlviHpAZXknRz7skYWPfcgO9v30A1SyA==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/config-resolver": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.5.tgz", - "integrity": "sha512-n0c2AXz+kjALY2FQr7Zy9zhYigXzboIh1AuUUVCqFBKFtdEvTwnwPXrTDoEehLiRTUHNL+4yzZ3s+D0kKYSLSg==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/credential-provider-imds": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.5.tgz", - "integrity": "sha512-KFcf/e0meFkQNyteJ65f1G19sgUEY1e5zL7hyAEUPz2SEfBmC9B37WyRq87G3MEEsvmAWwCRu7nFFYUKtR3svQ==", - "optional": true, - "requires": { - "@smithy/node-config-provider": "^2.0.5", - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "tslib": "^2.5.0" - } - }, - "@smithy/eventstream-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.5.tgz", - "integrity": "sha512-iqR6OuOV3zbQK8uVs9o+9AxhVk8kW9NAxA71nugwUB+kTY9C35pUd0A5/m4PRT0Y0oIW7W4kgnSR3fdYXQjECw==", - "optional": true, - "requires": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/fetch-http-handler": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.5.tgz", - "integrity": "sha512-EzFoMowdBNy1VqtvkiXgPFEdosIAt4/4bgZ8uiDiUyfhmNXq/3bV+CagPFFBsgFOR/X2XK4zFZHRsoa7PNHVVg==", - "optional": true, - "requires": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/querystring-builder": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/util-base64": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/hash-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.5.tgz", - "integrity": "sha512-mk551hIywBITT+kXruRNXk7f8Fy7DTzBjZJSr/V6nolYKmUHIG3w5QU6nO9qPYEQGKc/yEPtkpdS28ndeG93lA==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/invalid-dependency": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.5.tgz", - "integrity": "sha512-0wEi+JT0hM+UUwrJVYbqjuGFhy5agY/zXyiN7BNAJ1XoCDjU5uaNSj8ekPWsXd/d4yM6NSe8UbPd8cOc1+3oBQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-content-length": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.5.tgz", - "integrity": "sha512-E7VwV5H02fgZIUGRli4GevBCAPvkyEI/fgl9SU47nPPi3DAAX3nEtUb8xfGbXjOcJ5BdSUoWWZn42tEd/blOqA==", - "optional": true, - "requires": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-endpoint": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.5.tgz", - "integrity": "sha512-tyzDuoNTbsMQCq5Xkc4QOt6e2GACUllQIV8SQ5fc59FtOIV9/vbf58/GxVjZm2o8+MMbdDBANjTDZe/ijZKfyA==", - "optional": true, - "requires": { - "@smithy/middleware-serde": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-retry": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.5.tgz", - "integrity": "sha512-ulIfbFyzQTVnJbLjUl1CTSi0etg6tej/ekwaLp0Gn8ybUkDkKYa+uB6CF/m2J5B6meRwyJlsryR+DjaOVyiicg==", - "optional": true, - "requires": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/service-error-classification": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-retry": "^2.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.621.0.tgz", + "integrity": "sha512-FQbC7I8ae/72ZekLBa45jWJ+Q3d+YPhc3bW/rCks6RrldM6RgLTGr8pTOPCxHl828ky10RjkBiBmVU818rliyw==", + "requires": { + "@aws-sdk/client-cognito-identity": "3.621.0", + "@aws-sdk/client-sso": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/credential-provider-cognito-identity": "3.621.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-ini": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "optional": true - } - } - }, - "@smithy/middleware-serde": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.5.tgz", - "integrity": "sha512-in0AA5sous74dOfTGU9rMJBXJ0bDVNxwdXtEt5lh3FVd2sEyjhI+rqpLLRF1E4ixbw3RSEf80hfRpcPdjg4vvQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", - "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/node-config-provider": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.5.tgz", - "integrity": "sha512-LRtjV9WkhONe2lVy+ipB/l1GX60ybzBmFyeRUoLUXWKdnZ3o81jsnbKzMK8hKq8eFSWPk+Lmyx6ZzCQabGeLxg==", - "optional": true, - "requires": { - "@smithy/property-provider": "^2.0.5", - "@smithy/shared-ini-file-loader": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/node-http-handler": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.5.tgz", - "integrity": "sha512-lZm5DZf4b3V0saUw9WTC4/du887P6cy2fUyQgQQKRRV6OseButyD5yTzeMmXE53CaXJBMBsUvvIQ0hRVxIq56w==", - "optional": true, - "requires": { - "@smithy/abort-controller": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/querystring-builder": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.5.tgz", - "integrity": "sha512-cAFSUhX6aiHcmpWfrCLKvwBtgN1F6A0N8qY/8yeSi0LRLmhGqsY1/YTxFE185MCVzYbqBGXVr9TBv4RUcIV4rA==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/protocol-http": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.5.tgz", - "integrity": "sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-builder": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.5.tgz", - "integrity": "sha512-4DCX9krxLzATj+HdFPC3i8pb7XTAWzzKqSw8aTZMjXjtQY+vhe4azMAqIvbb6g7JKwIkmkRAjK6EXO3YWSnJVQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.5.tgz", - "integrity": "sha512-C2stCULH0r54KBksv3AWcN8CLS3u9+WsEW8nBrvctrJ5rQTNa1waHkffpVaiKvcW2nP0aIMBPCobD/kYf/q9mA==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/service-error-classification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", - "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", - "optional": true - }, - "@smithy/shared-ini-file-loader": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.5.tgz", - "integrity": "sha512-Mvtk6FwMtfbKRC4YuSsIqRYp9WTxsSUJVVo2djgyhcacKGMqicHDWSAmgy3sDrKv+G/G6xTZCPwm6pJARtdxVg==", - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/signature-v4": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.5.tgz", - "integrity": "sha512-ABIzXmUDXK4n2c9cXjQLELgH2RdtABpYKT+U131e2I6RbCypFZmxIHmIBufJzU2kdMCQ3+thBGDWorAITFW04A==", - "optional": true, - "requires": { - "@smithy/eventstream-codec": "^2.0.5", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/smithy-client": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.5.tgz", - "integrity": "sha512-kCTFr8wfOAWKDzGvfBElc6shHigWtHNhMQ1IbosjC4jOlayFyZMSs2PysKB+Ox/dhQ41KqOzgVjgiQ+PyWqHMQ==", - "optional": true, - "requires": { - "@smithy/middleware-stack": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-stream": "^2.0.5", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.2.2.tgz", - "integrity": "sha512-4PS0y1VxDnELGHGgBWlDksB2LJK8TG8lcvlWxIsgR+8vROI7Ms8h1P4FQUx+ftAX2QZv5g1CJCdhdRmQKyonyw==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/url-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.5.tgz", - "integrity": "sha512-OdMBvZhpckQSkugCXNJQCvqJ71wE7Ftxce92UOQLQ9pwF6hoS5PLL7wEfpnuEXtStzBqJYkzu1C1ZfjuFGOXAA==", - "optional": true, - "requires": { - "@smithy/querystring-parser": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", - "optional": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, - "requires": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.5.tgz", - "integrity": "sha512-yciP6TPttLsj731aHTvekgyuCGXQrEAJibEwEWAh3kzaDsfGAVCuZSBlyvC2Dl3TZmHKCOQwHV8mIE7KQCTPuQ==", - "optional": true, - "requires": { - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "bowser": "^2.11.0", - "tslib": "^2.5.0" + "@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "requires": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } + }, + "@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "requires": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "requires": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } + }, + "@aws-sdk/client-cognito-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.621.0.tgz", + "integrity": "sha512-FpXia5qFf6ijcNDWenVq+mP9r1LbiW/+52i9wrv2+Afi6Nn1ROf8W7St8WvE9TEZ3t78y+vis4CwqfGts+uiKA==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/client-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.621.0.tgz", + "integrity": "sha512-xpKfikN4u0BaUYZA9FGUMkkDmfoIP0Q03+A86WjqDWhcOoqNA1DkHsE4kZ+r064ifkPUfcNuUvlkVTEoBZoFjA==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/client-sts": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.621.0.tgz", + "integrity": "sha512-707uiuReSt+nAx6d0c21xLjLm2lxeKc7padxjv92CIrIocnQSlJPxSCM7r5zBhwiahJA6MNQwmTl2xznU67KgA==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-cognito-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.621.0.tgz", + "integrity": "sha512-Q+3awvTVJSqIGRjCUQflRwKPKlZ0TfmL3EQHgFLhZZrToeBapEA62+FY+T70aTKAZZZZprlvYeFPtBloNd5ziA==", + "requires": { + "@aws-sdk/client-cognito-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.621.0.tgz", + "integrity": "sha512-0EWVnSc+JQn5HLnF5Xv405M8n4zfdx9gyGdpnCmAmFqEDHA8LmBdxJdpUk1Ovp/I5oPANhjojxabIW5f1uU0RA==", + "requires": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.621.0.tgz", + "integrity": "sha512-4JqpccUgz5Snanpt2+53hbOBbJQrSFq7E1sAAbgY6BKVQUsW5qyXqnjvSF32kDeKa5JpBl3bBWLZl04IadcPHw==", + "requires": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-ini": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.621.0.tgz", + "integrity": "sha512-Kza0jcFeA/GEL6xJlzR2KFf1PfZKMFnxfGzJzl5yN7EjoGdMijl34KaRyVnfRjnCWcsUpBWKNIDk9WZVMY9yiw==", + "requires": { + "@aws-sdk/client-sso": "3.621.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz", + "integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/token-providers": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", + "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "requires": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/config-resolver": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", + "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "requires": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/credential-provider-imds": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "requires": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "requires": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/hash-node": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", + "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "requires": { + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "@smithy/invalid-dependency": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", + "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-content-length": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "requires": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "requires": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-retry": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.13.tgz", + "integrity": "sha512-zvCLfaRYCaUmjbF2yxShGZdolSHft7NNCTA28HVN9hKcEbOH+g5irr1X9s+in8EpambclGnevZY4A3lYpvDCFw==", + "requires": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + } + }, + "@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "requires": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "requires": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/service-error-classification": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", + "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "requires": { + "@smithy/types": "^3.3.0" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/smithy-client": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.11.tgz", + "integrity": "sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "requires": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "requires": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-browser": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.13.tgz", + "integrity": "sha512-ZIRSUsnnMRStOP6OKtW+gCSiVFkwnfQF2xtf32QKAbHR6ACjhbAybDvry+3L5qQYdh3H6+7yD/AiUE45n8mTTw==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-node": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.13.tgz", + "integrity": "sha512-voUa8TFJGfD+U12tlNNLCDlXibt9vRdNzRX45Onk/WxZe7TS+hTOZouEZRa7oARGicdgeXvt1A0W45qLGYdy+g==", + "requires": { + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-retry": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", + "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "requires": { + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "requires": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + } + } + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + } } }, - "@smithy/util-defaults-mode-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.5.tgz", - "integrity": "sha512-M07t99rWasXt+IaDZDyP3BkcoEm/mgIE1RIMASrE49LKSNxaVN7PVcgGc77+4uu2kzBAyqJKy79pgtezuknyjQ==", - "optional": true, + "@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", + "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", "requires": { - "@smithy/config-resolver": "^2.0.5", - "@smithy/credential-provider-imds": "^2.0.5", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "dependencies": { + "@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + } } }, - "@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, + "@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", "requires": { "tslib": "^2.5.0" } }, - "@smithy/util-middleware": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", - "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", + "@mongodb-js/saslprep": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", + "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", "optional": true, "requires": { - "tslib": "^2.5.0" + "sparse-bitfield": "^3.0.3" } }, - "@smithy/util-retry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", - "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", - "optional": true, - "requires": { - "@smithy/service-error-classification": "^2.0.0", - "tslib": "^2.5.0" + "@smithy/core": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.3.1.tgz", + "integrity": "sha512-BC7VMXx/1BCmRPCVzzn4HGWAtsrb7/0758EtwOGFJQrlSwJBEjCcDLNZLFoL/68JexYa2s+KmgL/UfmXdG6v1w==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "requires": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "requires": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-retry": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.13.tgz", + "integrity": "sha512-zvCLfaRYCaUmjbF2yxShGZdolSHft7NNCTA28HVN9hKcEbOH+g5irr1X9s+in8EpambclGnevZY4A3lYpvDCFw==", + "requires": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + } + }, + "@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "requires": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "requires": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/service-error-classification": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", + "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "requires": { + "@smithy/types": "^3.3.0" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/smithy-client": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.11.tgz", + "integrity": "sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "requires": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "requires": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-retry": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", + "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "requires": { + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "requires": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "requires": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + } } }, - "@smithy/util-stream": { + "@smithy/util-endpoints": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.5.tgz", - "integrity": "sha512-ylx27GwI05xLpYQ4hDIfS15vm+wYjNN0Sc2P0FxuzgRe8v0BOLHppGIQ+Bezcynk8C9nUzsUue3TmtRhjut43g==", - "optional": true, - "requires": { - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", - "optional": true, + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", + "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "requires": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "requires": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "requires": { + "tslib": "^2.6.2" + } + } } }, "@srfnstack/spliffy": { @@ -1032,8 +2665,7 @@ "bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "optional": true + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" }, "bson": { "version": "4.7.2", @@ -1082,15 +2714,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "optional": true, - "requires": { - "strnum": "^1.0.5" - } - }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1189,9 +2812,9 @@ } }, "mysql2": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.7.tgz", - "integrity": "sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw==", + "version": "3.9.8", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.8.tgz", + "integrity": "sha512-+5JKNjPuks1FNMoy9TYpl77f+5frbTklz7eb3XDwbpsERRLEeXiW2PDEkakYF50UuKU2qwfGnyXpKYvukv8mGA==", "requires": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -1435,8 +3058,7 @@ "strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "optional": true + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, "tr46": { "version": "3.0.0", @@ -1449,8 +3071,7 @@ "tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "optional": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "uWebSockets.js": { "version": "github:uNetworking/uWebSockets.js#7bf0faac5859fef2d113e83d22803f7833774c11", diff --git a/frameworks/JavaScript/spliffy/package.json b/frameworks/JavaScript/spliffy/package.json index bdb6b49aa45..62b2240acf3 100644 --- a/frameworks/JavaScript/spliffy/package.json +++ b/frameworks/JavaScript/spliffy/package.json @@ -6,7 +6,7 @@ "@srfnstack/spliffy": "0.6.1", "html-escaper": "3.0.3", "mongodb": "^4.17.0", - "mysql2": "^3.9.7", + "mysql2": "^3.9.8", "node-cache": "5.1.2", "pg": "8.6.0", "pg-native": "3.0.1" diff --git a/frameworks/JavaScript/uwebsockets.js/README.md b/frameworks/JavaScript/uwebsockets.js/README.md index 73fc530d673..bc59e8c3026 100644 --- a/frameworks/JavaScript/uwebsockets.js/README.md +++ b/frameworks/JavaScript/uwebsockets.js/README.md @@ -10,14 +10,12 @@ The tests were run with: - [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js/) - [postgres](https://github.com/porsager/postgres/) -- [mariadb](https://github.com/mariadb-corporation/mariadb-connector-nodejs/) ## Database There are individual handlers for each DB approach. The logic for each of them are found here: - [PostgreSQL](src/database/postgres.js) -- [MySQL](src/database/mysql.js) There are **no database endpoints** or drivers attached by default. @@ -25,7 +23,6 @@ To initialize the application with one of these, run any _one_ of the following ```sh $ DATABASE=postgres npm start -$ DATABASE=mysql npm start ``` ## Test Endpoints diff --git a/frameworks/JavaScript/uwebsockets.js/benchmark_config.json b/frameworks/JavaScript/uwebsockets.js/benchmark_config.json index c19862c849e..a0ac515dcfe 100644 --- a/frameworks/JavaScript/uwebsockets.js/benchmark_config.json +++ b/frameworks/JavaScript/uwebsockets.js/benchmark_config.json @@ -4,7 +4,7 @@ { "default": { "approach": "Realistic", - "classification": "Micro", + "classification": "Platform", "database": "None", "database_os": "Linux", "display_name": "uWebSockets.js", @@ -41,27 +41,6 @@ "update_url": "/updates?queries=", "versus": "nodejs", "webserver": "None" - }, - "mysql": { - "approach": "Realistic", - "classification": "Platform", - "database": "MySQL", - "database_os": "Linux", - "db_url": "/db", - "display_name": "uWebSockets.js", - "flavor": "NodeJS", - "fortune_url": "/fortunes", - "framework": "uWebSockets.js", - "language": "JavaScript", - "notes": "", - "orm": "Raw", - "os": "Linux", - "platform": "None", - "port": 8080, - "query_url": "/queries?queries=", - "update_url": "/updates?queries=", - "versus": "nodejs", - "webserver": "None" } } ] diff --git a/frameworks/JavaScript/uwebsockets.js/package-lock.json b/frameworks/JavaScript/uwebsockets.js/package-lock.json index 275f2f63b2e..13fff11e161 100644 --- a/frameworks/JavaScript/uwebsockets.js/package-lock.json +++ b/frameworks/JavaScript/uwebsockets.js/package-lock.json @@ -9,65 +9,9 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "mariadb": "^3.3.0", - "postgres": "^3.4.4", + "postgres": "3.4.4", "slow-json-stringify": "^2.0.1", - "uWebSockets.js": "uNetworking/uWebSockets.js#v20.43.0" - } - }, - "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==" - }, - "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "engines": { - "node": ">=0.10" - } - }, - "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==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/mariadb": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.3.0.tgz", - "integrity": "sha512-sAL4bJgbfCAtXcE8bXI+NAMzVaPNkIU8hRZUXYfgNFoWB9U57G3XQiMeCx/A6IrS6y7kGwBLylrwgsZQ8kUYlw==", - "dependencies": { - "@types/geojson": "^7946.0.14", - "@types/node": "^20.11.17", - "denque": "^2.1.0", - "iconv-lite": "^0.6.3", - "lru-cache": "^10.2.0" - }, - "engines": { - "node": ">= 14" + "uWebSockets.js": "uNetworking/uWebSockets.js#v20.44.0" } }, "node_modules/postgres": { @@ -82,24 +26,14 @@ "url": "https://github.com/sponsors/porsager" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, "node_modules/slow-json-stringify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/slow-json-stringify/-/slow-json-stringify-2.0.1.tgz", "integrity": "sha512-jqyzIqTaSkRGcWdWqjmOLKHZgOGUT71ZCTsvQu1xGu9Mqaod7O26y5FJJEmaUQhaTWh0bkXv2qqN0i+EQsD1jQ==" }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, "node_modules/uWebSockets.js": { - "version": "20.31.0", - "resolved": "git+ssh://git@github.com/uNetworking/uWebSockets.js.git#809b99d2d7d12e2cbf89b7135041e9b41ff84084" + "version": "20.44.0", + "resolved": "git+ssh://git@github.com/uNetworking/uWebSockets.js.git#8fa05571bf6ea95be8966ad313d9d39453e381ae" } } } diff --git a/frameworks/JavaScript/uwebsockets.js/package.json b/frameworks/JavaScript/uwebsockets.js/package.json index c64742e442d..635c0a2b12a 100644 --- a/frameworks/JavaScript/uwebsockets.js/package.json +++ b/frameworks/JavaScript/uwebsockets.js/package.json @@ -1,9 +1,8 @@ { "dependencies": { - "mariadb": "^3.3.0", - "postgres": "^3.4.4", + "postgres": "3.4.4", "slow-json-stringify": "^2.0.1", - "uWebSockets.js": "uNetworking/uWebSockets.js#v20.43.0" + "uWebSockets.js": "uNetworking/uWebSockets.js#v20.44.0" }, "license": "MIT", "main": "src/server.js", diff --git a/frameworks/JavaScript/uwebsockets.js/src/database/mysql.js b/frameworks/JavaScript/uwebsockets.js/src/database/mysql.js deleted file mode 100644 index a627a74d132..00000000000 --- a/frameworks/JavaScript/uwebsockets.js/src/database/mysql.js +++ /dev/null @@ -1,16 +0,0 @@ -import { createPool } from "mariadb"; -import os from "node:os"; - -const pool = createPool({ - host: "tfb-database", - user: "benchmarkdbuser", - password: "benchmarkdbpass", - database: "hello_world", - connectionLimit: os.availableParallelism() -}); - -export const fortunes = async () => await pool.execute("SELECT id, message FROM fortune"); - -export const find = async (id) => await pool.execute("SELECT id, randomnumber FROM world WHERE id = ?", [id]).then((arr) => arr[0]); - -export const bulkUpdate = async (worlds) => await Promise.all(worlds.map(world => pool.execute("UPDATE world SET randomnumber = ? WHERE id = ?", [world.randomNumber, world.id]))); diff --git a/frameworks/JavaScript/uwebsockets.js/src/database/postgres.js b/frameworks/JavaScript/uwebsockets.js/src/database/postgres.js index 105eeef99e5..db262a9757d 100644 --- a/frameworks/JavaScript/uwebsockets.js/src/database/postgres.js +++ b/frameworks/JavaScript/uwebsockets.js/src/database/postgres.js @@ -5,6 +5,7 @@ const sql = postgres({ user: "benchmarkdbuser", password: "benchmarkdbpass", database: "hello_world", + fetch_types: false, max: 1 }); diff --git a/frameworks/JavaScript/uwebsockets.js/src/server.js b/frameworks/JavaScript/uwebsockets.js/src/server.js index f67f33e874b..0305347a2cf 100644 --- a/frameworks/JavaScript/uwebsockets.js/src/server.js +++ b/frameworks/JavaScript/uwebsockets.js/src/server.js @@ -16,11 +16,13 @@ if (DATABASE) db = await import(`./database/${DATABASE}.js`); const webserver = uWebSockets.App(); -webserver.get("/plaintext", (response) => { - addBenchmarkHeaders(response); - response.writeHeader("Content-Type", "text/plain"); - response.end("Hello, World!"); -}); +uWebSockets._cfg('silent'); + +webserver.get("/plaintext", new uWebSockets.DeclarativeResponse() + .writeHeader("Server", "uWS") + .writeHeader("Content-Type", "text/plain") + .end("Hello, World!") +); webserver.get("/json", (response) => { addBenchmarkHeaders(response); @@ -90,7 +92,7 @@ if (db) { handleError(error, response); } }); - + const extra = { id: 0, message: "Additional fortune added at request time." }; webserver.get("/fortunes", async (response) => { diff --git a/frameworks/JavaScript/uwebsockets.js/uwebsockets.js-mysql.dockerfile b/frameworks/JavaScript/uwebsockets.js/uwebsockets.js-mysql.dockerfile deleted file mode 100644 index 8533d8b9a61..00000000000 --- a/frameworks/JavaScript/uwebsockets.js/uwebsockets.js-mysql.dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:20-slim - -COPY ./ ./ - -RUN npm install - -ENV NODE_ENV production -ENV DATABASE mysql - -EXPOSE 8080 - -CMD ["npm", "start"] diff --git a/frameworks/JavaScript/velocy/package.json b/frameworks/JavaScript/velocy/package.json index a51441b5c26..320f6058e7a 100644 --- a/frameworks/JavaScript/velocy/package.json +++ b/frameworks/JavaScript/velocy/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "velocy": "0.0.13" + "velocy": "0.0.14" } } diff --git a/frameworks/Julia/oxygen/README.md b/frameworks/Julia/oxygen/README.md new file mode 100755 index 00000000000..5b7ac109195 --- /dev/null +++ b/frameworks/Julia/oxygen/README.md @@ -0,0 +1,21 @@ +# Oxygen.jl Benchmarking Test + +Oxygen is a micro-framework built on top of the HTTP.jl library and comes with helpful utilities to quickly setup and run web applications in Julia. + +### Test Type Implementation Source Code + +* [JSON](Relative/Path/To/Your/Source/File) +* [PLAINTEXT](Relative/Path/To/Your/Source/File) + +## Important Libraries +The tests were run with: +* [Oxygen.jl](https://github.com/OxygenFramework/Oxygen.jl) + +## Test URLs +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext diff --git a/frameworks/Julia/oxygen/benchmark_config.json b/frameworks/Julia/oxygen/benchmark_config.json new file mode 100755 index 00000000000..e998bb13773 --- /dev/null +++ b/frameworks/Julia/oxygen/benchmark_config.json @@ -0,0 +1,26 @@ +{ + "framework": "oxygen", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "None", + "database": "None", + "framework": "Oxygen.jl", + "language": "Julia", + "orm": "None", + "platform": "None", + "webserver": "Oxygen.jl", + "os": "Linux", + "database_os": "Linux", + "display_name": "Oxygen.jl", + "notes": "", + "versus": "", + "tags": [] + } + } + ] +} \ No newline at end of file diff --git a/frameworks/Julia/oxygen/oxygen.dockerfile b/frameworks/Julia/oxygen/oxygen.dockerfile new file mode 100644 index 00000000000..df34f0a4b38 --- /dev/null +++ b/frameworks/Julia/oxygen/oxygen.dockerfile @@ -0,0 +1,8 @@ +FROM julia:latest + +WORKDIR /app +COPY ./src ./ +RUN julia --project -e 'using Pkg; Pkg.instantiate()' + +EXPOSE 8080 +CMD julia -t 2 --project server.jl \ No newline at end of file diff --git a/frameworks/Julia/oxygen/src/Project.toml b/frameworks/Julia/oxygen/src/Project.toml new file mode 100644 index 00000000000..dbe190679c2 --- /dev/null +++ b/frameworks/Julia/oxygen/src/Project.toml @@ -0,0 +1,4 @@ +[deps] +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +Oxygen = "df9a0d86-3283-4920-82dc-4555fc0d1d8b" diff --git a/frameworks/Julia/oxygen/src/server.jl b/frameworks/Julia/oxygen/src/server.jl new file mode 100644 index 00000000000..e88c1669472 --- /dev/null +++ b/frameworks/Julia/oxygen/src/server.jl @@ -0,0 +1,23 @@ + +using Oxygen +using Dates +using HTTP + +@get "/json" function() + return json(("message" => "Hello, World!")) +end + +@get "/plaintext" function() + return text("Hello, World!") +end + +function HeaderMiddleware(handle::Function) + function(req::HTTP.Request) + response = handle(req) + HTTP.setheader(response, "Server" => "Julia-Oxygen") + HTTP.setheader(response, "Date" => Dates.format(Dates.now(), Dates.RFC1123Format) * " GMT") + return response + end +end + +serveparallel(host="0.0.0.0", port=8080, middleware=[HeaderMiddleware], access_log=nothing, metrics=false, docs=false) \ No newline at end of file diff --git a/frameworks/Kotlin/hexagon/build.gradle b/frameworks/Kotlin/hexagon/build.gradle index 9279d3b38fd..d1f77b2b178 100644 --- a/frameworks/Kotlin/hexagon/build.gradle +++ b/frameworks/Kotlin/hexagon/build.gradle @@ -1,7 +1,7 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.9.23" apply false - id "org.graalvm.buildtools.native" version "0.10.1" apply false + id "org.jetbrains.kotlin.jvm" version "2.0.20" apply false + id "org.graalvm.buildtools.native" version "0.10.3" apply false } version = "1.0.0" @@ -9,13 +9,13 @@ description = "TFB benchmark" group = "com.hexagonkt" ext { - hexagonVersion = "3.5.1" - jettyVersion = "12.0.7" - nettyVersion = "4.1.107.Final" + hexagonVersion = "3.7.0" + jettyVersion = "12.0.13" + nettyVersion = "4.1.113.Final" hikariVersion = "5.1.0" - postgresqlVersion = "42.7.3" - vertxVersion = "4.5.5" + postgresqlVersion = "42.7.4" + vertxVersion = "4.5.10" cache2kVersion = "2.6.1.Final" applicationClass = "com.hexagonkt.BenchmarkKt" @@ -30,5 +30,5 @@ subprojects { } tasks.wrapper { - gradleVersion = "8.6" + gradleVersion = "8.10" } diff --git a/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.jar b/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.jar index d64cd491770..2c3521197d7 100644 Binary files a/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.jar and b/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.jar differ diff --git a/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.properties b/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.properties index a80b22ce5cf..9355b415575 100644 --- a/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.properties +++ b/frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/frameworks/Kotlin/hexagon/gradlew b/frameworks/Kotlin/hexagon/gradlew index 1aa94a42690..f5feea6d6b1 100755 --- a/frameworks/Kotlin/hexagon/gradlew +++ b/frameworks/Kotlin/hexagon/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/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +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 diff --git a/frameworks/Kotlin/hexagon/gradlew.bat b/frameworks/Kotlin/hexagon/gradlew.bat index 25da30dbdee..9d21a21834d 100644 --- a/frameworks/Kotlin/hexagon/gradlew.bat +++ b/frameworks/Kotlin/hexagon/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 ########################################################################## diff --git a/frameworks/Kotlin/hexagon/hexagon-helidon-native.dockerfile b/frameworks/Kotlin/hexagon/hexagon-helidon-native.dockerfile index c402f0ee792..25a78904f79 100644 --- a/frameworks/Kotlin/hexagon/hexagon-helidon-native.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-helidon-native.dockerfile @@ -16,6 +16,8 @@ RUN ./gradlew --quiet -x test hexagon_helidon_pgclient:nativeCompile FROM scratch ARG PROJECT=hexagon_helidon_pgclient +ENV maximumPoolSize 300 + COPY --from=build /hexagon/$PROJECT/build/native/nativeCompile/$PROJECT / ENTRYPOINT [ "/hexagon_helidon_pgclient" ] diff --git a/frameworks/Kotlin/hexagon/hexagon-helidon-pgclient.dockerfile b/frameworks/Kotlin/hexagon/hexagon-helidon-pgclient.dockerfile index d87e78307b0..f11c4e7b2bd 100644 --- a/frameworks/Kotlin/hexagon/hexagon-helidon-pgclient.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-helidon-pgclient.dockerfile @@ -1,22 +1,23 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-22-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test installDist +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test installDist # # RUNTIME # -FROM docker.io/eclipse-temurin:21-jre-alpine +FROM docker.io/bellsoft/liberica-runtime-container:jre-22-musl ARG PROJECT=hexagon_helidon_pgclient ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS --enable-preview -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA +ENV maximumPoolSize 300 COPY --from=build /hexagon/$PROJECT/build/install/$PROJECT /opt/$PROJECT diff --git a/frameworks/Kotlin/hexagon/hexagon-helidon.dockerfile b/frameworks/Kotlin/hexagon/hexagon-helidon.dockerfile index 913a9459a06..27177de5eb9 100644 --- a/frameworks/Kotlin/hexagon/hexagon-helidon.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-helidon.dockerfile @@ -1,22 +1,23 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-22-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test installDist +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test installDist # # RUNTIME # -FROM docker.io/eclipse-temurin:21-jre-alpine +FROM docker.io/bellsoft/liberica-runtime-container:jre-22-musl ARG PROJECT=hexagon_helidon_postgresql ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS --enable-preview -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA +ENV maximumPoolSize 300 COPY --from=build /hexagon/$PROJECT/build/install/$PROJECT /opt/$PROJECT diff --git a/frameworks/Kotlin/hexagon/hexagon-jetty-native.dockerfile b/frameworks/Kotlin/hexagon/hexagon-jetty-native.dockerfile index 2919fdba8b7..b8efc965eb3 100644 --- a/frameworks/Kotlin/hexagon/hexagon-jetty-native.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-jetty-native.dockerfile @@ -16,6 +16,8 @@ RUN ./gradlew --quiet -x test hexagon_jetty_postgresql:nativeCompile FROM scratch ARG PROJECT=hexagon_jetty_postgresql +ENV maximumPoolSize 300 + COPY --from=build /hexagon/$PROJECT/build/native/nativeCompile/$PROJECT / ENTRYPOINT [ "/hexagon_jetty_postgresql" ] diff --git a/frameworks/Kotlin/hexagon/hexagon-jettyloom-pgclient.dockerfile b/frameworks/Kotlin/hexagon/hexagon-jettyloom-pgclient.dockerfile index 90d51b0b30b..21ef3b64c8b 100644 --- a/frameworks/Kotlin/hexagon/hexagon-jettyloom-pgclient.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-jettyloom-pgclient.dockerfile @@ -1,22 +1,23 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-21-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test installDist +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test installDist # # RUNTIME # -FROM docker.io/eclipse-temurin:21-jre-alpine +FROM docker.io/bellsoft/liberica-runtime-container:jre-21-musl ARG PROJECT=hexagon_jetty_pgclient ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS --enable-preview -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA -DvirtualThreads=true +ENV maximumPoolSize 300 COPY --from=build /hexagon/$PROJECT/build/install/$PROJECT /opt/$PROJECT diff --git a/frameworks/Kotlin/hexagon/hexagon-jettyloom.dockerfile b/frameworks/Kotlin/hexagon/hexagon-jettyloom.dockerfile index 93b0e22a963..c51c03bc6db 100644 --- a/frameworks/Kotlin/hexagon/hexagon-jettyloom.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-jettyloom.dockerfile @@ -1,22 +1,23 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-21-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test installDist +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test installDist # # RUNTIME # -FROM docker.io/eclipse-temurin:21-jre-alpine +FROM docker.io/bellsoft/liberica-runtime-container:jre-21-musl ARG PROJECT=hexagon_jetty_postgresql ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS --enable-preview -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA -DvirtualThreads=true +ENV maximumPoolSize 300 COPY --from=build /hexagon/$PROJECT/build/install/$PROJECT /opt/$PROJECT diff --git a/frameworks/Kotlin/hexagon/hexagon-nettyepoll-pgclient.dockerfile b/frameworks/Kotlin/hexagon/hexagon-nettyepoll-pgclient.dockerfile index ba9b55020b9..90c5a0d03c8 100644 --- a/frameworks/Kotlin/hexagon/hexagon-nettyepoll-pgclient.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-nettyepoll-pgclient.dockerfile @@ -1,22 +1,23 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-21-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test installDist +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test installDist # # RUNTIME # -FROM docker.io/eclipse-temurin:21-jre-alpine +FROM docker.io/bellsoft/liberica-runtime-container:jre-21-musl ARG PROJECT=hexagon_nettyepoll_pgclient ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA +ENV maximumPoolSize 300 COPY --from=build /hexagon/$PROJECT/build/install/$PROJECT /opt/$PROJECT diff --git a/frameworks/Kotlin/hexagon/hexagon-nettyepoll.dockerfile b/frameworks/Kotlin/hexagon/hexagon-nettyepoll.dockerfile index a7c28730e03..35a0e4729b7 100644 --- a/frameworks/Kotlin/hexagon/hexagon-nettyepoll.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-nettyepoll.dockerfile @@ -1,22 +1,23 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-21-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test installDist +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test installDist # # RUNTIME # -FROM docker.io/eclipse-temurin:21-jre-alpine +FROM docker.io/bellsoft/liberica-runtime-container:jre-21-musl ARG PROJECT=hexagon_nettyepoll_postgresql ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA +ENV maximumPoolSize 300 COPY --from=build /hexagon/$PROJECT/build/install/$PROJECT /opt/$PROJECT diff --git a/frameworks/Kotlin/hexagon/hexagon-tomcat.dockerfile b/frameworks/Kotlin/hexagon/hexagon-tomcat.dockerfile index abd537c0dd3..42f28071128 100644 --- a/frameworks/Kotlin/hexagon/hexagon-tomcat.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon-tomcat.dockerfile @@ -1,21 +1,22 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-21-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test war +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test war # # RUNTIME # -FROM docker.io/tomcat:10-jre21-temurin-jammy +FROM docker.io/tomcat:11.0.0-jre21-temurin-noble ARG MODULE=/hexagon/hexagon_tomcat_postgresql ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA +ENV maximumPoolSize 300 COPY --from=build $MODULE/build/libs/ROOT.war /usr/local/tomcat/webapps/ROOT.war diff --git a/frameworks/Kotlin/hexagon/hexagon.dockerfile b/frameworks/Kotlin/hexagon/hexagon.dockerfile index a8eec2e6809..2654c706db1 100644 --- a/frameworks/Kotlin/hexagon/hexagon.dockerfile +++ b/frameworks/Kotlin/hexagon/hexagon.dockerfile @@ -1,22 +1,23 @@ # # BUILD # -FROM docker.io/gradle:8.6-jdk21-alpine AS build +FROM docker.io/bellsoft/liberica-runtime-container:jdk-all-21-cds-musl AS build USER root WORKDIR /hexagon ADD . . -RUN gradle --quiet classes -RUN gradle --quiet -x test installDist +RUN ./gradlew --quiet classes +RUN ./gradlew --quiet -x test installDist # # RUNTIME # -FROM docker.io/eclipse-temurin:21-jre-alpine +FROM docker.io/bellsoft/liberica-runtime-container:jre-21-musl ARG PROJECT=hexagon_jetty_postgresql ENV POSTGRESQL_DB_HOST tfb-database ENV JDK_JAVA_OPTIONS -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA +ENV maximumPoolSize 300 COPY --from=build /hexagon/$PROJECT/build/install/$PROJECT /opt/$PROJECT diff --git a/frameworks/Kotlin/hexagon/hexagon_helidon_pgclient/src/main/kotlin/Benchmark.kt b/frameworks/Kotlin/hexagon/hexagon_helidon_pgclient/src/main/kotlin/Benchmark.kt index d38d33281f8..a5778a2c353 100644 --- a/frameworks/Kotlin/hexagon/hexagon_helidon_pgclient/src/main/kotlin/Benchmark.kt +++ b/frameworks/Kotlin/hexagon/hexagon_helidon_pgclient/src/main/kotlin/Benchmark.kt @@ -1,18 +1,30 @@ package com.hexagonkt +import com.hexagonkt.core.Jvm.systemSettingOrNull import com.hexagonkt.core.media.TEXT_HTML import com.hexagonkt.core.urlOf import com.hexagonkt.http.server.helidon.HelidonServerAdapter import com.hexagonkt.store.BenchmarkPgClientStore import com.hexagonkt.templates.jte.JteAdapter +import java.time.Duration fun main() { - val settings = Settings() val store = BenchmarkPgClientStore("postgresql") val templateEngine = JteAdapter(TEXT_HTML, precompiled = true) val templateUrl = urlOf("classpath:fortunes.jte") - val engine = HelidonServerAdapter() + val engine = HelidonServerAdapter( + backlog = systemSettingOrNull("backlog") ?: (8 * 1024), + writeQueueLength = systemSettingOrNull("writeQueueLength") ?: (8 * 1024), + readTimeout = Duration.parse(systemSettingOrNull("readTimeout") ?: "PT0S"), + connectTimeout = Duration.parse(systemSettingOrNull("connectTimeout") ?: "PT0S"), + tcpNoDelay = systemSettingOrNull("tcpNoDelay") ?: true, + receiveLog = systemSettingOrNull("receiveLog") ?: false, + sendLog = systemSettingOrNull("sendLog") ?: false, + validatePath = systemSettingOrNull("validatePath") ?: false, + validateRequestHeaders = systemSettingOrNull("validateRequestHeaders") ?: false, + validateResponseHeaders = systemSettingOrNull("validateResponseHeaders") ?: false, + ) - val benchmark = Benchmark(engine, store, templateEngine, templateUrl, settings) + val benchmark = Benchmark(engine, store, templateEngine, templateUrl, Settings()) benchmark.server.start() } diff --git a/frameworks/Kotlin/hexagon/hexagon_tomcat_postgresql/src/main/kotlin/WebListenerServer.kt b/frameworks/Kotlin/hexagon/hexagon_tomcat_postgresql/src/main/kotlin/WebListenerServer.kt index 69fe6d13ba6..c7d7ea4449c 100644 --- a/frameworks/Kotlin/hexagon/hexagon_tomcat_postgresql/src/main/kotlin/WebListenerServer.kt +++ b/frameworks/Kotlin/hexagon/hexagon_tomcat_postgresql/src/main/kotlin/WebListenerServer.kt @@ -6,6 +6,7 @@ import com.hexagonkt.http.model.Header import com.hexagonkt.http.model.Headers import com.hexagonkt.http.handlers.HttpHandler import com.hexagonkt.http.handlers.OnHandler +import com.hexagonkt.http.handlers.PathHandler import com.hexagonkt.http.server.servlet.ServletServer import com.hexagonkt.store.BenchmarkSqlStore import com.hexagonkt.templates.jte.JteAdapter @@ -18,7 +19,7 @@ import jakarta.servlet.annotation.WebListener private companion object { val headers = Headers(Header("server", "Tomcat")) - fun createHandlers(settings: Settings): List { + fun createHandlers(settings: Settings): HttpHandler { val store = BenchmarkSqlStore("postgresql") val templateEngine = JteAdapter(TEXT_HTML, precompiled = true) val templateUrl = urlOf("classpath:fortunes.jte") @@ -28,7 +29,7 @@ import jakarta.servlet.annotation.WebListener send(headers = headers) } - return listOf(serverHeaderHandler, controllerPath) + return PathHandler(serverHeaderHandler, controllerPath) } } } diff --git a/frameworks/Kotlin/hexagon/store_pgclient/src/main/kotlin/BenchmarkPgClientStore.kt b/frameworks/Kotlin/hexagon/store_pgclient/src/main/kotlin/BenchmarkPgClientStore.kt index 1fb9547353f..510abda0295 100644 --- a/frameworks/Kotlin/hexagon/store_pgclient/src/main/kotlin/BenchmarkPgClientStore.kt +++ b/frameworks/Kotlin/hexagon/store_pgclient/src/main/kotlin/BenchmarkPgClientStore.kt @@ -6,8 +6,10 @@ import com.hexagonkt.model.CachedWorld import com.hexagonkt.model.Fortune import com.hexagonkt.model.World import io.vertx.core.Future +import io.vertx.core.Vertx +import io.vertx.core.VertxOptions +import io.vertx.pgclient.PgBuilder import io.vertx.pgclient.PgConnectOptions -import io.vertx.pgclient.PgPool import io.vertx.sqlclient.* import org.cache2k.Cache @@ -35,11 +37,15 @@ class BenchmarkPgClientStore( private val poolOptions: PoolOptions by lazy { PoolOptions().apply { val environment = Jvm.systemSettingOrNull("BENCHMARK_ENV")?.lowercase() - maxSize = 8 + if (environment == "citrine") Jvm.cpuCount else Jvm.cpuCount * 2 + val poolSize = 8 + if (environment == "citrine") Jvm.cpuCount else Jvm.cpuCount * 2 + maxSize = Jvm.systemSettingOrNull(Int::class, "maximumPoolSize") ?: poolSize } } - private val dataSource: SqlClient by lazy { PgPool.client(connectOptions, poolOptions) } + private val dataSource: SqlClient by lazy { + val vertx = Vertx.vertx(VertxOptions().setPreferNativeTransport(true)) + PgBuilder.client().using(vertx).connectingTo(connectOptions).with(poolOptions).build() + } override fun findAllFortunes(): List = dataSource.preparedQuery(SELECT_ALL_FORTUNES) diff --git a/frameworks/Kotlin/kooby/kooby.dockerfile b/frameworks/Kotlin/kooby/kooby.dockerfile index 56c278c467a..678fc57b8d4 100644 --- a/frameworks/Kotlin/kooby/kooby.dockerfile +++ b/frameworks/Kotlin/kooby/kooby.dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.9.0-eclipse-temurin-17 +FROM maven:3.9.9-eclipse-temurin-22-alpine WORKDIR /kooby COPY pom.xml pom.xml COPY src src diff --git a/frameworks/Kotlin/kooby/pom.xml b/frameworks/Kotlin/kooby/pom.xml index 44397e8f522..4ff8df958da 100644 --- a/frameworks/Kotlin/kooby/pom.xml +++ b/frameworks/Kotlin/kooby/pom.xml @@ -12,12 +12,12 @@ kooby: jooby+kotlin - 3.0.5 - 42.7.2 + 3.3.0 + 42.7.4 UTF-8 - 17 - 17 - 1.8.21 + 22 + 22 + 2.0.20 kooby.AppKt @@ -52,7 +52,7 @@ com.mysql mysql-connector-j - 8.0.33 + 9.0.0 @@ -77,7 +77,7 @@ com.fizzed rocker-maven-plugin - 1.3.0 + 1.4.0 generate-rocker-templates @@ -98,7 +98,7 @@ kotlin-maven-plugin ${kotlin.version} - 17 + 22 @@ -127,7 +127,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.13.0 @@ -159,7 +159,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.0 uber-jar diff --git a/frameworks/Kotlin/kooby/src/main/kotlin/kooby/App.kt b/frameworks/Kotlin/kooby/src/main/kotlin/kooby/App.kt index edc43137b1b..958991e29d3 100644 --- a/frameworks/Kotlin/kooby/src/main/kotlin/kooby/App.kt +++ b/frameworks/Kotlin/kooby/src/main/kotlin/kooby/App.kt @@ -33,7 +33,7 @@ fun main(args: Array) { runApp(args, EVENT_LOOP) { /** Template engine: */ - install(RockerModule().reuseBuffer(true)) + install(RockerModule()) /** JSON: */ install(JacksonModule()) diff --git a/frameworks/Kotlin/ktor/benchmark_config.json b/frameworks/Kotlin/ktor/benchmark_config.json index f675237f4b9..3a213a240ba 100644 --- a/frameworks/Kotlin/ktor/benchmark_config.json +++ b/frameworks/Kotlin/ktor/benchmark_config.json @@ -94,29 +94,6 @@ "notes": "", "versus": "netty" }, - "reactivepg": { - "json_url": "/json", - "plaintext_url": "/plaintext", - "db_url": "/db", - "query_url": "/query/?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "Postgres", - "framework": "Ktor", - "language": "Kotlin", - "flavor": "None", - "orm": "Raw", - "platform": "None", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "Ktor-reactivepg", - "notes": "", - "versus": "netty" - }, "pgclient": { "plaintext_url": "/plaintext", "json_url": "/json", diff --git a/frameworks/Kotlin/ktor/config.toml b/frameworks/Kotlin/ktor/config.toml index 6077323686d..1c58e63c73f 100644 --- a/frameworks/Kotlin/ktor/config.toml +++ b/frameworks/Kotlin/ktor/config.toml @@ -35,23 +35,6 @@ platform = "None" webserver = "None" versus = "netty" -[reactivepg] -urls.plaintext = "/plaintext" -urls.json = "/json" -urls.db = "/db" -urls.query = "/query/?queries=" -urls.update = "/updates?queries=" -urls.fortune = "/fortunes" -approach = "Realistic" -classification = "Fullstack" -database = "Postgres" -database_os = "Linux" -os = "Linux" -orm = "Raw" -platform = "None" -webserver = "None" -versus = "netty" - [cio] urls.plaintext = "/plaintext" urls.json = "/json" diff --git a/frameworks/Kotlin/ktor/ktor-asyncdb/build.gradle b/frameworks/Kotlin/ktor/ktor-asyncdb/build.gradle deleted file mode 100644 index 9a7b3bf67ee..00000000000 --- a/frameworks/Kotlin/ktor/ktor-asyncdb/build.gradle +++ /dev/null @@ -1,41 +0,0 @@ -plugins { - id "java" - id "application" - id 'org.jetbrains.kotlin.jvm' - id 'kotlinx-serialization' - id 'com.github.johnrengelman.shadow' version '4.0.3' -} - -group 'org.jetbrains.ktor' -version '1.0-SNAPSHOT' - -mainClassName = "MainKt" - -repositories { - mavenCentral() - jcenter() - maven { url "https://kotlin.bintray.com/kotlinx" } -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.9.1" - compile "io.ktor:ktor-server-netty:$ktor_version" - compile "io.ktor:ktor-html-builder:$ktor_version" - compile "com.github.jasync-sql:jasync-postgresql:0.9.39" - compile "io.reactiverse:reactive-pg-client:0.11.3" - compile 'io.vertx:vertx-lang-kotlin-coroutines:3.7.0' -} - -compileKotlin { - kotlinOptions.jvmTarget = "1.8" -} -compileTestKotlin { - kotlinOptions.jvmTarget = "1.8" -} - -shadowJar { - baseName = "bench" - classifier = null - version = null -} diff --git a/frameworks/Kotlin/ktor/ktor-asyncdb/build.gradle.kts b/frameworks/Kotlin/ktor/ktor-asyncdb/build.gradle.kts new file mode 100644 index 00000000000..61cedf013ce --- /dev/null +++ b/frameworks/Kotlin/ktor/ktor-asyncdb/build.gradle.kts @@ -0,0 +1,36 @@ +plugins { + application + kotlin("jvm") version "1.9.22" + kotlin("plugin.serialization") version "2.0.0" + id("com.github.johnrengelman.shadow") version "8.1.0" +} + +group = "org.jetbrains.ktor" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +application { + mainClass.set("MainKt") +} + +val ktor_version = "2.3.12" +val kotlinx_serialization_version = "1.6.3" +val vertx_pg_client = "4.5.8" + +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_version") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") + implementation("io.ktor:ktor-server-netty:$ktor_version") + implementation("io.ktor:ktor-server-default-headers:$ktor_version") + implementation("io.ktor:ktor-server-html-builder:$ktor_version") + implementation("com.github.jasync-sql:jasync-postgresql:2.2.0") +} + +tasks.shadowJar { + archiveBaseName.set("bench") + archiveClassifier.set("") + archiveVersion.set("") +} diff --git a/frameworks/Kotlin/ktor/ktor-asyncdb/gradle.properties b/frameworks/Kotlin/ktor/ktor-asyncdb/gradle.properties index f8ca4442eea..5790d58ceda 100644 --- a/frameworks/Kotlin/ktor/ktor-asyncdb/gradle.properties +++ b/frameworks/Kotlin/ktor/ktor-asyncdb/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official -kotlin_version=1.3.31 -ktor_version=1.2.0 +kotlin_version=1.9.22 +ktor_version=2.3.12 diff --git a/frameworks/Kotlin/ktor/ktor-asyncdb/gradle/wrapper/gradle-wrapper.properties b/frameworks/Kotlin/ktor/ktor-asyncdb/gradle/wrapper/gradle-wrapper.properties index e15293214f9..3d66c176054 100644 --- a/frameworks/Kotlin/ktor/ktor-asyncdb/gradle/wrapper/gradle-wrapper.properties +++ b/frameworks/Kotlin/ktor/ktor-asyncdb/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip diff --git a/frameworks/Kotlin/ktor/ktor-asyncdb/src/main/kotlin/main.kt b/frameworks/Kotlin/ktor/ktor-asyncdb/src/main/kotlin/main.kt index ca7d9e5713e..e9f4221d8ed 100644 --- a/frameworks/Kotlin/ktor/ktor-asyncdb/src/main/kotlin/main.kt +++ b/frameworks/Kotlin/ktor/ktor-asyncdb/src/main/kotlin/main.kt @@ -1,28 +1,21 @@ import com.github.jasync.sql.db.ConnectionPoolConfiguration +import com.github.jasync.sql.db.QueryResult import com.github.jasync.sql.db.SuspendingConnection import com.github.jasync.sql.db.asSuspending import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder -import io.ktor.application.call -import io.ktor.application.install -import io.ktor.features.DefaultHeaders -import io.ktor.html.Placeholder -import io.ktor.html.Template -import io.ktor.html.insert -import io.ktor.html.respondHtmlTemplate import io.ktor.http.ContentType -import io.ktor.response.respondText -import io.ktor.routing.get -import io.ktor.routing.routing +import io.ktor.server.application.* import io.ktor.server.engine.embeddedServer +import io.ktor.server.html.* import io.ktor.server.netty.Netty -import io.reactiverse.kotlin.pgclient.getConnectionAwait -import io.reactiverse.kotlin.pgclient.preparedBatchAwait -import io.reactiverse.kotlin.pgclient.preparedQueryAwait -import io.reactiverse.pgclient.* +import io.ktor.server.plugins.defaultheaders.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import kotlinx.coroutines.* import kotlinx.html.* import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JSON -import kotlinx.serialization.list +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import java.lang.IllegalArgumentException import kotlin.random.Random import kotlin.random.nextInt @@ -44,73 +37,48 @@ interface Repository { } class JasyncRepository() : Repository { - private val dbConfig: ConnectionPoolConfiguration - private val db: SuspendingConnection - - init { - dbConfig = ConnectionPoolConfiguration( - "tfb-database", - database = "hello_world", - username = "benchmarkdbuser", - password = "benchmarkdbpass", - maxActiveConnections = 64 - ) - db = PostgreSQLConnectionBuilder.createConnectionPool(dbConfig).asSuspending + companion object { + const val WORLD_QUERY = "select id, randomNumber from world where id = ?" + const val FORTUNES_QUERY = "select id, message from fortune" + const val UPDATE_QUERY = "update world set randomNumber = ? where id = ?" } + private val dbConfig: ConnectionPoolConfiguration = ConnectionPoolConfiguration( + "tfb-database", + database = "hello_world", + username = "benchmarkdbuser", + password = "benchmarkdbpass", + maxActiveConnections = 64 + ) + private val db: SuspendingConnection = PostgreSQLConnectionBuilder.createConnectionPool(dbConfig).asSuspending + override suspend fun getWorld(): World { val worldId = rand.nextInt(1, 10000) - val result = db.sendPreparedStatement("select id, randomNumber from world where id = ?", listOf(worldId)) + val result = db.sendPreparedStatement(WORLD_QUERY, listOf(worldId)) val row = result.rows.first() return World(row.getInt(0)!!, row.getInt(1)!!) } override suspend fun getFortunes(): List { - val results = db.sendPreparedStatement("select id, message from fortune") + val results = db.sendPreparedStatement(FORTUNES_QUERY) return results.rows.map { Fortune(it.getInt(0)!!, it.getString(1)!!) } } override suspend fun updateWorlds(worlds: List) { - worlds.forEach { world -> - db.sendPreparedStatement( - "update world set randomNumber = ? where id = ?", - listOf(world.randomNumber, world.id) - ) - } - } -} + coroutineScope { + val jobs = ArrayList>(worlds.size) + worlds.forEach { world -> + val deferred = async(Dispatchers.IO) { + db.sendPreparedStatement( + UPDATE_QUERY, + listOf(world.randomNumber, world.id) + ) + } + jobs.add(deferred) + } -class ReactivePGRepository : Repository { - private val db: PgPool - - init { - val poolOptions = PgPoolOptions() - poolOptions.apply { - host = "tfb-database" - database = "hello_world" - user = "benchmarkdbuser" - password = "benchmarkdbpass" - maxSize = 64 - cachePreparedStatements = true + jobs.awaitAll() } - db = PgClient.pool(poolOptions) - } - - override suspend fun getFortunes(): List { - val results = db.preparedQueryAwait("select id, message from fortune") - return results.map { Fortune(it.getInteger(0), it.getString(1)) } - } - - override suspend fun getWorld(): World { - val worldId = rand.nextInt(1, 10000) - val result = db.preparedQueryAwait("select id, randomNumber from world where id = $1", Tuple.of(worldId)) - val row = result.first() - return World(row.getInteger(0), row.getInteger(1)!!) - } - - override suspend fun updateWorlds(worlds: List) { - val batch = worlds.map { Tuple.of(it.id, it.randomNumber) } - db.preparedBatchAwait("update world set randomNumber = $1 where id = $2", batch) } } @@ -132,7 +100,7 @@ class MainTemplate : Template { } } -class FortuneTemplate(val fortunes: List, val main: MainTemplate = MainTemplate()) : Template { +class FortuneTemplate(private val fortunes: List, private val main: MainTemplate = MainTemplate()) : Template { override fun HTML.apply() { insert(main) { content { @@ -156,13 +124,9 @@ class FortuneTemplate(val fortunes: List, val main: MainTemplate = Main fun main(args: Array) { val db = when(args.firstOrNull()) { "jasync-sql" -> JasyncRepository() - "reactive-pg" -> ReactivePGRepository() else -> throw IllegalArgumentException("Must specify a postgres client") } - val messageSerializer = Message.serializer() - val worldSerializer = World.serializer() - val server = embeddedServer(Netty, 8080, configure = { shareWorkGroup = true }) { @@ -174,19 +138,19 @@ fun main(args: Array) { get("/json") { call.respondText( - JSON.stringify(messageSerializer, Message("Hello, World!")), + Json.encodeToString(Message("Hello, World!")), ContentType.Application.Json ) } get("/db") { - call.respondText(JSON.stringify(worldSerializer, db.getWorld()), ContentType.Application.Json) + call.respondText(Json.encodeToString(db.getWorld()), ContentType.Application.Json) } get("/query/") { val queries = call.parameters["queries"]?.toBoxedInt(1..500) ?: 1 val worlds = (1..queries).map { db.getWorld() } - call.respondText(JSON.stringify(worldSerializer.list, worlds), ContentType.Application.Json) + call.respondText(Json.encodeToString(worlds), ContentType.Application.Json) } get("/fortunes") { @@ -204,7 +168,7 @@ fun main(args: Array) { db.updateWorlds(newWorlds) - call.respondText(JSON.stringify(worldSerializer.list, newWorlds), ContentType.Application.Json) + call.respondText(Json.encodeToString(newWorlds), ContentType.Application.Json) } } } diff --git a/frameworks/Kotlin/ktor/ktor-cio.dockerfile b/frameworks/Kotlin/ktor/ktor-cio.dockerfile index 6ba40b6eccc..33059ec9fd4 100644 --- a/frameworks/Kotlin/ktor/ktor-cio.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-cio.dockerfile @@ -1,10 +1,10 @@ -FROM maven:3.6.1-jdk-11-slim as maven +FROM maven:3.9.7-amazoncorretto-17-debian as maven WORKDIR /ktor COPY ktor/pom.xml pom.xml COPY ktor/src src RUN mvn clean package -q -FROM openjdk:11.0.3-jdk-stretch +FROM amazoncorretto:17.0.11-al2023-headless WORKDIR /ktor COPY --from=maven /ktor/target/tech-empower-framework-benchmark-1.0-SNAPSHOT-cio-bundle.jar app.jar diff --git a/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile b/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile index 9867cf2931c..10f2ed2180d 100644 --- a/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile @@ -1,9 +1,9 @@ -FROM gradle:8.0.2-jdk11 +FROM gradle:jdk17 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 5b7f7fe722a..5d05b4494ce 100644 --- a/frameworks/Kotlin/ktor/ktor-exposed-dsl.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-exposed-dsl.dockerfile @@ -1,9 +1,9 @@ -FROM gradle:8.0.2-jdk11 +FROM gradle:jdk17 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 4d798b53bcf..f57ded6c314 100644 --- a/frameworks/Kotlin/ktor/ktor-exposed/app/build.gradle.kts +++ b/frameworks/Kotlin/ktor/ktor-exposed/app/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - kotlin("jvm") version "1.8.10" - kotlin("plugin.serialization") version "1.8.10" application + kotlin("jvm") version "1.9.22" + 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.2.3" -val kotlinxSerializationVersion = "1.5.0" -val exposedVersion = "0.41.1" +val ktorVersion = "2.3.12" +val kotlinxSerializationVersion = "1.6.3" +val exposedVersion = "0.56.0" dependencies { implementation("io.ktor:ktor-server-core:$ktorVersion") 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-jasync.dockerfile b/frameworks/Kotlin/ktor/ktor-jasync.dockerfile index 38649f62693..8c66e81ea4e 100644 --- a/frameworks/Kotlin/ktor/ktor-jasync.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-jasync.dockerfile @@ -1,7 +1,7 @@ -FROM openjdk:11.0.3-jdk-stretch +FROM maven:3.9.7-amazoncorretto-17-debian WORKDIR /app COPY ktor-asyncdb/gradle gradle -COPY ktor-asyncdb/build.gradle build.gradle +COPY ktor-asyncdb/build.gradle.kts build.gradle.kts COPY ktor-asyncdb/gradle.properties gradle.properties COPY ktor-asyncdb/gradlew gradlew COPY ktor-asyncdb/settings.gradle settings.gradle diff --git a/frameworks/Kotlin/ktor/ktor-jetty.dockerfile b/frameworks/Kotlin/ktor/ktor-jetty.dockerfile index 3976e5f2df9..e753d7cc442 100644 --- a/frameworks/Kotlin/ktor/ktor-jetty.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-jetty.dockerfile @@ -1,10 +1,10 @@ -FROM maven:3.6.1-jdk-11-slim as maven +FROM maven:3.9.7-amazoncorretto-17-debian as maven WORKDIR /ktor COPY ktor/pom.xml pom.xml COPY ktor/src src RUN mvn clean package -q -FROM openjdk:11.0.3-jdk-stretch +FROM amazoncorretto:17.0.11-al2023-headless WORKDIR /ktor COPY --from=maven /ktor/target/tech-empower-framework-benchmark-1.0-SNAPSHOT-jetty-bundle.jar app.jar diff --git a/frameworks/Kotlin/ktor/ktor-pgclient.dockerfile b/frameworks/Kotlin/ktor/ktor-pgclient.dockerfile index be123f07e6f..0cf012e7596 100644 --- a/frameworks/Kotlin/ktor/ktor-pgclient.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-pgclient.dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:11.0.3-jdk-stretch as build +FROM maven:3.9.7-amazoncorretto-17-debian as build WORKDIR /app COPY ktor-pgclient/gradle gradle COPY ktor-pgclient/build.gradle.kts build.gradle.kts @@ -6,7 +6,7 @@ COPY ktor-pgclient/gradlew gradlew COPY ktor-pgclient/src src RUN /app/gradlew --no-daemon shadowJar -FROM openjdk:11.0.3-jdk-slim +FROM amazoncorretto:17.0.11-al2023-headless WORKDIR /app COPY --from=build /app/build/libs/ktor-pgclient.jar ktor-pgclient.jar diff --git a/frameworks/Kotlin/ktor/ktor-pgclient/build.gradle.kts b/frameworks/Kotlin/ktor/ktor-pgclient/build.gradle.kts index f57c372b49f..f080b64d69f 100644 --- a/frameworks/Kotlin/ktor/ktor-pgclient/build.gradle.kts +++ b/frameworks/Kotlin/ktor/ktor-pgclient/build.gradle.kts @@ -1,8 +1,8 @@ plugins { application - kotlin("jvm") version "1.6.10" - id("org.jetbrains.kotlin.plugin.serialization") version "1.6.21" - id("com.github.johnrengelman.shadow") version "7.1.2" + kotlin("jvm") version "1.9.22" + kotlin("plugin.serialization") version "2.0.0" + id("com.github.johnrengelman.shadow") version "8.1.0" } group = "org.jetbrains.ktor" @@ -16,19 +16,20 @@ application { mainClass.set("MainKt") } +val ktor_version = "2.3.12" + dependencies { - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - implementation("io.ktor:ktor-server-netty:2.0.1") - implementation("io.ktor:ktor-server-html-builder-jvm:2.0.1") - implementation("io.ktor:ktor-server-default-headers-jvm:2.0.1") - implementation("io.vertx:vertx-pg-client:4.2.3") - implementation("io.vertx:vertx-lang-kotlin:4.2.3") - implementation("io.vertx:vertx-lang-kotlin-coroutines:4.2.3") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("io.ktor:ktor-server-netty:$ktor_version") + implementation("io.ktor:ktor-server-html-builder-jvm:$ktor_version") + implementation("io.ktor:ktor-server-default-headers-jvm:$ktor_version") + implementation("io.vertx:vertx-pg-client:4.5.8") + implementation("io.vertx:vertx-lang-kotlin:4.5.8") + implementation("io.vertx:vertx-lang-kotlin-coroutines:4.5.8") } tasks.withType().configureEach { - kotlinOptions.jvmTarget = "11" + kotlinOptions.jvmTarget = "17" } tasks.shadowJar { diff --git a/frameworks/Kotlin/ktor/ktor-pgclient/gradle/wrapper/gradle-wrapper.properties b/frameworks/Kotlin/ktor/ktor-pgclient/gradle/wrapper/gradle-wrapper.properties index aa991fceae6..e1bef7e873c 100644 --- a/frameworks/Kotlin/ktor/ktor-pgclient/gradle/wrapper/gradle-wrapper.properties +++ b/frameworks/Kotlin/ktor/ktor-pgclient/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/frameworks/Kotlin/ktor/ktor-pgclient/src/main/kotlin/main.kt b/frameworks/Kotlin/ktor/ktor-pgclient/src/main/kotlin/main.kt index b4f7dc89615..c8e74244f99 100644 --- a/frameworks/Kotlin/ktor/ktor-pgclient/src/main/kotlin/main.kt +++ b/frameworks/Kotlin/ktor/ktor-pgclient/src/main/kotlin/main.kt @@ -7,6 +7,8 @@ import io.ktor.server.plugins.defaultheaders.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.vertx.kotlin.coroutines.await +import io.vertx.kotlin.coroutines.coAwait +import io.vertx.pgclient.PgBuilder import io.vertx.pgclient.PgConnectOptions import io.vertx.pgclient.PgPool import io.vertx.sqlclient.PoolOptions @@ -35,6 +37,12 @@ interface Repository { } class PgclientRepository : Repository { + companion object { + private const val FORTUNES_QUERY = "select id, message from FORTUNE" + private const val SELECT_WORLD_QUERY = "SELECT id, randomnumber from WORLD where id=$1" + private const val UPDATE_WORLD_QUERY = "UPDATE WORLD SET randomnumber=$1 WHERE id=$2" + } + private val connectOptions = PgConnectOptions().apply { port = 5432 @@ -47,21 +55,23 @@ class PgclientRepository : Repository { } private val poolOptions = PoolOptions() - private val client = ThreadLocal.withInitial { PgPool.client(connectOptions, poolOptions) } - private fun client() = client.get() + private val client = PgBuilder.client() + .with(poolOptions) + .connectingTo(connectOptions) + .build() override suspend fun getFortunes(): List { - val results = client().preparedQuery("select id, message from fortune").execute().await() + val results = client.preparedQuery(FORTUNES_QUERY).execute().coAwait() return results.map { Fortune(it.getInteger(0), it.getString(1)) } } override suspend fun getWorld(): World { val worldId = rand.nextInt(1, 10001) val result = - client() - .preparedQuery("select id, randomNumber from world where id = $1") + client + .preparedQuery(SELECT_WORLD_QUERY) .execute(Tuple.of(worldId)) - .await() + .coAwait() val row = result.first() return World(row.getInteger(0), row.getInteger(1)!!) } @@ -69,10 +79,10 @@ class PgclientRepository : Repository { override suspend fun updateWorlds(worlds: List) { // Worlds should be sorted before being batch-updated with to avoid data race and deadlocks. val batch = worlds.sortedBy { it.id }.map { Tuple.of(it.randomNumber, it.id) } - client() - .preparedQuery("update world set randomNumber = $1 where id = $2") + client + .preparedQuery(UPDATE_WORLD_QUERY) .executeBatch(batch) - .await() + .coAwait() } } diff --git a/frameworks/Kotlin/ktor/ktor-reactivepg.dockerfile b/frameworks/Kotlin/ktor/ktor-reactivepg.dockerfile deleted file mode 100644 index 41476da82ba..00000000000 --- a/frameworks/Kotlin/ktor/ktor-reactivepg.dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM openjdk:11.0.3-jdk-stretch -WORKDIR /app -COPY ktor-asyncdb/gradle gradle -COPY ktor-asyncdb/build.gradle build.gradle -COPY ktor-asyncdb/gradle.properties gradle.properties -COPY ktor-asyncdb/gradlew gradlew -COPY ktor-asyncdb/settings.gradle settings.gradle -COPY ktor-asyncdb/src src -RUN /app/gradlew --no-daemon shadowJar - -EXPOSE 9090 - -CMD ["java", "-server", "-XX:+UseParallelGC", "-Xms2G","-Xmx2G", "-jar", "/app/build/libs/bench.jar", "reactive-pg"] diff --git a/frameworks/Kotlin/ktor/ktor.dockerfile b/frameworks/Kotlin/ktor/ktor.dockerfile index 36aa32fd8c2..1558d312736 100644 --- a/frameworks/Kotlin/ktor/ktor.dockerfile +++ b/frameworks/Kotlin/ktor/ktor.dockerfile @@ -1,13 +1,13 @@ -FROM maven:3.6.1-jdk-11-slim as maven +FROM maven:3.9.7-amazoncorretto-17-debian as maven WORKDIR /ktor COPY ktor/pom.xml pom.xml COPY ktor/src src RUN mvn clean package -q -FROM openjdk:11.0.3-jdk-stretch +FROM amazoncorretto:17.0.11-al2023-headless WORKDIR /ktor COPY --from=maven /ktor/target/tech-empower-framework-benchmark-1.0-SNAPSHOT-netty-bundle.jar app.jar EXPOSE 9090 -CMD ["java", "-server","-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-XX:+AlwaysPreTouch", "-jar", "app.jar"] +CMD ["java", "-server","-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AlwaysPreTouch", "-jar", "app.jar"] diff --git a/frameworks/Kotlin/ktor/ktor/README.md b/frameworks/Kotlin/ktor/ktor/README.md index 07cb92359c2..e3c141a70e0 100644 --- a/frameworks/Kotlin/ktor/ktor/README.md +++ b/frameworks/Kotlin/ktor/ktor/README.md @@ -5,13 +5,13 @@ More information is available at [ktor.io](http://ktor.io). # Setup -* Java 8 +* Java 17 * MySQL server # Requirements * Maven 3 -* JDK 8 +* JDK 17 * Kotlin * ktor * netty diff --git a/frameworks/Kotlin/ktor/ktor/pom.xml b/frameworks/Kotlin/ktor/ktor/pom.xml index 35ad4547a16..a794bb59fe3 100644 --- a/frameworks/Kotlin/ktor/ktor/pom.xml +++ b/frameworks/Kotlin/ktor/ktor/pom.xml @@ -12,10 +12,10 @@ org.jetbrains.ktor tech-empower-framework-benchmark - 1.6.21 - 2.0.1 - 1.3.2 - 0.7.3 + 1.9.22 + 2.3.11 + 1.6.3 + 0.11.0 UTF-8 5.0.0 1.2.13 @@ -24,11 +24,6 @@ - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${kotlin.version} - org.jetbrains.kotlin kotlin-reflect @@ -85,17 +80,17 @@ io.ktor ktor-server-netty-jvm - 2.0.0 + ${ktor.version} io.ktor ktor-server-jetty-jvm - 2.0.0 + ${ktor.version} io.ktor ktor-server-cio-jvm - 2.0.0 + ${ktor.version} 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 b767e0605f5..82bdd34bc66 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 @@ -9,12 +9,14 @@ import io.ktor.server.html.* import io.ktor.server.plugins.defaultheaders.* import io.ktor.server.response.* import io.ktor.server.routing.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import kotlinx.html.* import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import org.jetbrains.ktor.benchmarks.Constants.FORTUNES_QUERY +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 @@ -30,7 +32,7 @@ data class Fortune(val id: Int, var message: String) fun Application.main() { val dbRows = 10000 val poolSize = 48 - val pool by lazy { HikariDataSource(HikariConfig().apply { configurePostgres(poolSize) }) } + val pool = HikariDataSource(HikariConfig().apply { configurePostgres(poolSize) }) val databaseDispatcher = Dispatchers.IO install(DefaultHeaders) @@ -51,7 +53,7 @@ fun Application.main() { val world = withContext(databaseDispatcher) { pool.connection.use { connection -> - connection.prepareStatement("SELECT id, randomNumber FROM World WHERE id = ?").use { statement -> + connection.prepareStatement(WORLD_QUERY).use { statement -> statement.setInt(1, random.nextInt(dbRows) + 1) statement.executeQuery().use { rs -> @@ -67,7 +69,7 @@ fun Application.main() { fun Connection.selectWorlds(queries: Int, random: ThreadLocalRandom): List { val result = ArrayList(queries) - prepareStatement("SELECT id, randomNumber FROM World WHERE id = ?").use { statement -> + prepareStatement(WORLD_QUERY).use { statement -> repeat(queries) { statement.setInt(1, random.nextInt(dbRows) + 1) @@ -96,7 +98,7 @@ fun Application.main() { val result = mutableListOf() withContext(databaseDispatcher) { pool.connection.use { connection -> - connection.prepareStatement("SELECT id, message FROM fortune").use { statement -> + connection.prepareStatement(FORTUNES_QUERY).use { statement -> statement.executeQuery().use { rs -> while (rs.next()) { result += Fortune(rs.getInt(1), rs.getString(2)) @@ -137,7 +139,7 @@ fun Application.main() { result.forEach { it.randomNumber = random.nextInt(dbRows) + 1 } - connection.prepareStatement("UPDATE World SET randomNumber = ? WHERE id = ?") + connection.prepareStatement(UPDATE_QUERY) .use { updateStatement -> for ((id, randomNumber) in result) { updateStatement.setInt(1, randomNumber) @@ -182,3 +184,10 @@ fun HikariConfig.configureMySql(poolSize: Int) { fun ApplicationCall.queries() = request.queryParameters["queries"]?.toIntOrNull()?.coerceIn(1, 500) ?: 1 + + +object Constants { + const val WORLD_QUERY = "SELECT id, randomNumber FROM World WHERE id = ?" + const val FORTUNES_QUERY = "SELECT id, message FROM fortune" + const val UPDATE_QUERY = "UPDATE World SET randomNumber = ? WHERE id = ?" +} \ No newline at end of file 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-kotlin-dsljson/.gitattributes b/frameworks/Kotlin/vertx-web-kotlin-dsljson/.gitattributes new file mode 100644 index 00000000000..097f9f98d9e --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/.gitattributes @@ -0,0 +1,9 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/README.md b/frameworks/Kotlin/vertx-web-kotlin-dsljson/README.md new file mode 100644 index 00000000000..b8ae3f33b30 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/README.md @@ -0,0 +1,36 @@ +# Vert.x-Web Kotlin Dsljson Benchmarking Test + +Vert.x-Web in Kotlin with Dsljson serialization + +The code is written as a realistic server implementation: +- Code is organized logically into packages +- Repositories are created for each database entity to handler all operations pertaining to a specific table +- Handlers map to the logical entities which they serve +- JSON serialization is provided via Dsljson + - Doing this effectively required a custom Vert.x Buffer implementation that also extended OutputStream in order to rely on the efficient Vert.x heap memory pool instead of building a novel implementation. + +## Test URLs + +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext + +### DB + +http://localhost:8080/db + +### QUERY + +http://localhost:8080/query?queries= + +### UPDATE + +http://localhost:8080/update?queries= + +### FORTUNES + +http://localhost:8080/fortunes diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/benchmark_config.json b/frameworks/Kotlin/vertx-web-kotlin-dsljson/benchmark_config.json new file mode 100644 index 00000000000..445d7e5f319 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/benchmark_config.json @@ -0,0 +1,47 @@ +{ + "framework": "vertx-web-kotlin-dsljson", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "None", + "framework": "vertx-web", + "language": "Kotlin", + "flavor": "None", + "orm": "Raw", + "platform": "Vert.x", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "vertx-web-kotlin-dsljson", + "notes": "", + "versus": "vertx-web" + }, + "postgresql": { + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "postgres", + "framework": "vertx-web", + "language": "Kotlin", + "flavor": "None", + "orm": "Raw", + "platform": "Vert.x", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "vertx-web-kotlin-dsljson-postgresql", + "notes": "", + "versus": "vertx-web-postgres" + } + } + ] +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/build.gradle.kts b/frameworks/Kotlin/vertx-web-kotlin-dsljson/build.gradle.kts new file mode 100644 index 00000000000..5131d1b9ec7 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/build.gradle.kts @@ -0,0 +1,86 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + kotlin("jvm") version "1.9.22" + kotlin("kapt") version "1.9.22" + application + id("com.github.johnrengelman.shadow") version "7.1.2" +} + +group = "com.example" +version = "1.0.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} + +kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_21 + } + jvmToolchain(21) +} + +val mainClassName = "com.example.starter.App" + +val vertxVersion = "4.5.9" +val nettyVersion = "4.1.112.Final" +val scramVersion = "2.1" +val dslJsonVersion = "2.0.2" +val htmlFlowVersion = "4.6" +val log4jVersion = "2.23.1" + +application { + mainClass = mainClassName +} + +dependencies { + listOfNotNull( + // Kotlin + kotlin("stdlib-jdk8"), + kotlin("reflect"), + + // Vertx + platform("io.vertx:vertx-stack-depchain:$vertxVersion"), + "io.vertx:vertx-core", + "io.vertx:vertx-web", + "io.vertx:vertx-pg-client", + "io.vertx:vertx-lang-kotlin", + "io.vertx:vertx-lang-kotlin-coroutines", + + // Netty + "io.netty:netty-transport-native-epoll:$nettyVersion:linux-x86_64", + + // Postgres + "com.ongres.scram:client:$scramVersion", + + // dsljson + "com.dslplatform:dsl-json:$dslJsonVersion", + + // HtmlFlow + "com.github.xmlet:htmlflow:$htmlFlowVersion", + + // Logging + "org.apache.logging.log4j:log4j-core:$log4jVersion", + "org.apache.logging.log4j:log4j-api:$log4jVersion", + "org.apache.logging.log4j:log4j-api-kotlin:1.4.0", + "com.lmax:disruptor:4.0.0", + ).map { implementation(it) } + + listOf( + "com.dslplatform:dsl-json:$dslJsonVersion", + "org.apache.logging.log4j:log4j-core:$log4jVersion", + ).map { kapt(it) } +} + +tasks.withType { + archiveClassifier = "fat" + mergeServiceFiles() +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/configuration/scripts/server.sh b/frameworks/Kotlin/vertx-web-kotlin-dsljson/configuration/scripts/server.sh new file mode 100644 index 00000000000..a5b03908062 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/configuration/scripts/server.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +JVM_OPTS="-server \ + -Xms2G \ + -Xmx2G \ + -XX:+AlwaysPreTouch \ + -XX:+UseParallelGC \ + -XX:+PreserveFramePointer \ + -XX:+EnableDynamicAgentLoading \ + -XX:InitialCodeCacheSize=512m \ + -XX:ReservedCodeCacheSize=512m \ + -XX:MaxInlineLevel=20 \ + -XX:+UseNUMA \ + -Djava.lang.Integer.IntegerCache.high=10000 \ + -Dvertx.disableMetrics=true \ + -Dvertx.disableH2c=true \ + -Dvertx.disableWebsockets=true \ + -Dvertx.flashPolicyHandler=false \ + -Dvertx.threadChecks=false \ + -Dvertx.disableContextTimings=true \ + -Dvertx.disableTCCL=true \ + -Dvertx.disableHttpHeadersValidation=true \ + -Dvertx.eventLoopPoolSize=$((`grep --count ^processor /proc/cpuinfo`)) \ + -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \ + -Dio.netty.buffer.checkBounds=false \ + -Dio.netty.buffer.checkAccessible=false \ + -Dtfb.hasDB=false" + +JAR_PATH="./build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar" + +cleanup() { + echo "Caught SIGINT signal. Stopping the Java program..." + if [ ! -z "$JAVA_PID" ]; then + kill -SIGTERM "$JAVA_PID" + wait "$JAVA_PID" + fi + exit 0 +} + +trap cleanup SIGINT + +java $JVM_OPTS -jar $JAR_PATH & +JAVA_PID=$! + +echo "Server PID: $JAVA_PID" + +wait "$JAVA_PID" diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle.properties b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle.properties new file mode 100644 index 00000000000..f97ebb7d334 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle.properties @@ -0,0 +1 @@ +org.gradle.parallel=true diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.jar b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..2c3521197d7 Binary files /dev/null and b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.jar differ diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.properties b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..09523c0e549 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew new file mode 100644 index 00000000000..f5feea6d6b1 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# 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/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# 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 + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + 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. +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=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# 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" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew.bat b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew.bat new file mode 100644 index 00000000000..9d21a21834d --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@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 ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +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 + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +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 + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/settings.gradle.kts b/frameworks/Kotlin/vertx-web-kotlin-dsljson/settings.gradle.kts new file mode 100644 index 00000000000..a5d0428ce6f --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "vertx-web-kotlin-dsljson-benchmark" diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/App.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/App.kt new file mode 100644 index 00000000000..ee66a635231 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/App.kt @@ -0,0 +1,50 @@ +package com.example.starter + +import com.example.starter.utils.PeriodicDateResolver +import com.example.starter.utils.block +import io.vertx.core.Vertx +import io.vertx.kotlin.core.deploymentOptionsOf +import io.vertx.kotlin.core.vertxOptionsOf +import kotlin.time.Duration.Companion.seconds +import org.apache.logging.log4j.kotlin.Logging + +object App : Logging { + private const val SERVER_NAME = "Vert.x-Web Benchmark" + + @JvmStatic + fun main(args: Array?) { + val eventLoopPoolSize = System.getProperty("vertx.eventLoopPoolSize")?.toInt() + ?: Runtime.getRuntime().availableProcessors() + + val vertx = Vertx.vertx( + vertxOptionsOf( + eventLoopPoolSize = eventLoopPoolSize, + preferNativeTransport = true, + ) + ) + vertx.exceptionHandler { + logger.error(it) { "Vertx unexpected exception:" } + vertx.close().block(5.seconds) + } + + // Add the SIGINT handler + Runtime.getRuntime().addShutdownHook(Thread { + vertx.close().block(5.seconds) + }) + + // Initialize the periodic date resolver + PeriodicDateResolver.init(vertx) + + // Check the type of test that is being run + val hasDb = System.getProperty("tfb.hasDB")?.toBoolean() ?: false + + vertx.deployVerticle( + { if (hasDb) PostgresVerticle() else BasicVerticle() }, + deploymentOptionsOf( + instances = eventLoopPoolSize, + ) + ) + + logger.info("$SERVER_NAME started.") + } +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/BasicVerticle.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/BasicVerticle.kt new file mode 100644 index 00000000000..7870a146c78 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/BasicVerticle.kt @@ -0,0 +1,56 @@ +package com.example.starter + +import com.example.starter.handlers.DefaultHandler +import com.example.starter.handlers.MessageHandler +import com.example.starter.io.JsonResource +import com.example.starter.utils.isConnectionReset +import io.vertx.core.AbstractVerticle +import io.vertx.core.Promise +import io.vertx.core.http.HttpServerOptions +import io.vertx.ext.web.Router +import org.apache.logging.log4j.kotlin.Logging + +class BasicVerticle : AbstractVerticle() { + override fun start(startPromise: Promise) { + val defaultHandler = DefaultHandler() + val messageHandler = MessageHandler() + + val router = Router.router(vertx) + + router + .get("/plaintext") + .handler(defaultHandler::plaintext) + + router + .get("/json") + .handler(messageHandler::readDefaultMessage) + + val server = vertx + .createHttpServer(HTTP_SERVER_OPTIONS) + .requestHandler(router) + .exceptionHandler { + if (it.isConnectionReset()) return@exceptionHandler + logger.error(it) { "Exception in HttpServer" } + } + + server + .listen() + .onSuccess { + logger.info { "HTTP server started on port 8080" } + startPromise.complete() + } + .onFailure { + logger.error(it.cause) { "Failed to start" } + startPromise.fail(it.cause) + } + } + + companion object : Logging { + private const val HTTP_SERVER_OPTIONS_RESOURCE = "http-server-options.json" + + private val HTTP_SERVER_OPTIONS: HttpServerOptions by lazy { + val json = JsonResource.of(HTTP_SERVER_OPTIONS_RESOURCE) + HttpServerOptions(json) + } + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/PostgresVerticle.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/PostgresVerticle.kt new file mode 100644 index 00000000000..4caa057b488 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/PostgresVerticle.kt @@ -0,0 +1,81 @@ +package com.example.starter + +import com.example.starter.db.FortuneRepository +import com.example.starter.db.WorldRepository +import com.example.starter.handlers.FortuneHandler +import com.example.starter.handlers.WorldHandler +import com.example.starter.io.JsonResource +import com.example.starter.utils.isConnectionReset +import io.vertx.core.AbstractVerticle +import io.vertx.core.Promise +import io.vertx.core.http.HttpServerOptions +import io.vertx.ext.web.Router +import io.vertx.pgclient.PgConnectOptions +import io.vertx.pgclient.PgConnection +import org.apache.logging.log4j.kotlin.Logging + +class PostgresVerticle : AbstractVerticle() { + override fun start(startPromise: Promise) { + PgConnection.connect(vertx, PG_CONNECT_OPTIONS) + .onSuccess { conn -> + val fortuneHandler = FortuneHandler(FortuneRepository(conn)) + val worldHandler = WorldHandler(WorldRepository(conn)) + + val router = Router.router(vertx) + + router + .get("/fortunes") + .handler(fortuneHandler::templateFortunes) + + router + .get("/db") + .handler(worldHandler::readRandomWorld) + + router + .get("/queries") + .handler(worldHandler::readRandomWorlds) + + router + .get("/updates") + .handler(worldHandler::updateRandomWorlds) + + val server = vertx + .createHttpServer(HTTP_SERVER_OPTIONS) + .requestHandler(router) + .exceptionHandler { + if (it.isConnectionReset()) return@exceptionHandler + logger.error(it) { "Exception in HttpServer" } + } + + server + .listen() + .onSuccess { + logger.info { "HTTP server started on port 8080" } + startPromise.complete() + } + .onFailure { + logger.error(it) { "Failed to start" } + startPromise.fail(it) + } + } + .onFailure { + logger.error(it) { "Failed to start" } + startPromise.fail(it) + } + } + + companion object : Logging { + private const val HTTP_SERVER_OPTIONS_RESOURCE = "http-server-options.json" + private const val PG_CONNECT_OPTIONS_RESOURCE = "pg-connect-options.json" + + private val HTTP_SERVER_OPTIONS: HttpServerOptions by lazy { + val json = JsonResource.of(HTTP_SERVER_OPTIONS_RESOURCE) + HttpServerOptions(json) + } + + private val PG_CONNECT_OPTIONS: PgConnectOptions by lazy { + val json = JsonResource.of(PG_CONNECT_OPTIONS_RESOURCE) + PgConnectOptions(json) + } + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/AbstractRepository.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/AbstractRepository.kt new file mode 100644 index 00000000000..f2dec9a5f84 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/AbstractRepository.kt @@ -0,0 +1,7 @@ +package com.example.starter.db + +import io.vertx.pgclient.PgConnection + +abstract class AbstractRepository( + protected val conn: PgConnection +) \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/FortuneRepository.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/FortuneRepository.kt new file mode 100644 index 00000000000..b9e4b9ab784 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/FortuneRepository.kt @@ -0,0 +1,23 @@ +package com.example.starter.db + +import com.example.starter.models.Fortune +import com.example.starter.utils.mapToArray + +import io.vertx.core.Future +import io.vertx.pgclient.PgConnection +import io.vertx.sqlclient.Row + +class FortuneRepository(conn: PgConnection) : AbstractRepository(conn) { + private val selectFortuneQuery = this.conn.preparedQuery(SELECT_FORTUNE_SQL) + + fun selectFortunes(): Future> = selectFortuneQuery + .execute() + .map { it.mapToArray(FortuneRepository::map) } + + companion object { + private const val SELECT_FORTUNE_SQL = "SELECT id, message FROM fortune" + + @Suppress("NOTHING_TO_INLINE") + private inline fun map(row: Row): Fortune = Fortune(row.getInteger(0), row.getString(1)) + } +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/WorldRepository.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/WorldRepository.kt new file mode 100644 index 00000000000..74823141453 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/WorldRepository.kt @@ -0,0 +1,84 @@ +package com.example.starter.db + +import com.example.starter.models.World +import io.vertx.core.Future +import io.vertx.core.Promise +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 io.vertx.sqlclient.impl.ArrayTuple +import io.vertx.sqlclient.impl.SqlClientInternal +import java.util.concurrent.ThreadLocalRandom +import java.util.concurrent.atomic.AtomicInteger + +@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") +class WorldRepository(conn: PgConnection) : AbstractRepository(conn) { + private val selectWorldQuery = this.conn.preparedQuery(SELECT_WORLD_SQL) + private val updateWorldQueries = generateQueries(this.conn) + + fun selectRandomWorld(): Future = selectWorldQuery + .execute(Tuple.of(randomWorld())) + .map { map(it.iterator().next()) } + + fun selectRandomWorlds(numWorlds: Int): Future> { + val promise = Promise.promise>() + val arr = arrayOfNulls(numWorlds) + val count = AtomicInteger(0) + (this.conn as SqlClientInternal).group { c -> + repeat(numWorlds) { + c.preparedQuery(SELECT_WORLD_SQL).execute(Tuple.of(randomWorld())) { ar -> + val index = count.getAndIncrement() + arr[index] = map(ar.result().iterator().next()) + if (index == numWorlds - 1) { + promise.complete(arr as Array) + } + } + } + } + return promise.future() + } + + fun updateRandomWorlds(numWorlds: Int): Future> = selectRandomWorlds(numWorlds) + .flatMap { worlds -> + val params = ArrayTuple(worlds.size * 3) + worlds.forEach { + it.randomNumber = randomWorld() + params.addValue(it.id) + params.addValue(it.randomNumber) + } + worlds.forEach { + params.addValue(it.id) + } + updateWorldQueries[numWorlds - 1].execute(params).map { worlds } + } + + companion object { + private const val SELECT_WORLD_SQL = "SELECT id, randomnumber FROM world WHERE id = $1" + + private inline fun randomWorld(): Int = 1 + ThreadLocalRandom.current().nextInt(10000) + private inline fun map(row: Row): World = World(row.getInteger(0), row.getInteger(1)) + + private fun generateQueries(conn: PgConnection): Array>> { + val arr = arrayOfNulls>>(500) + for (num in 1..500) { + var paramIndex = 1 + val sb = StringBuilder() + sb.append("UPDATE world SET randomnumber = CASE id ") + for (i in 1..num) { + sb.append("WHEN \$$paramIndex THEN \$${paramIndex + 1} ") + paramIndex += 2 + } + sb.append("ELSE randomnumber END WHERE id IN (") + for (i in 1..num) { + sb.append("\$$paramIndex,") + paramIndex += 1 + } + sb[sb.length - 1] = ')' + arr[num - 1] = conn.preparedQuery(sb.toString()) + } + return arr as Array>> + } + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/AbstractHandler.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/AbstractHandler.kt new file mode 100644 index 00000000000..cb29d630167 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/AbstractHandler.kt @@ -0,0 +1,57 @@ +package com.example.starter.handlers + +import com.example.starter.utils.PeriodicDateResolver +import io.vertx.core.AsyncResult +import io.vertx.core.Handler +import io.vertx.core.MultiMap +import io.vertx.core.http.HttpHeaders +import io.vertx.core.http.HttpServerResponse +import io.vertx.ext.web.RoutingContext + +@Suppress("NOTHING_TO_INLINE") +abstract class AbstractHandler { + protected companion object { + val NULL_HANDLER: Handler>? = null + + const val SOMETHING_WENT_WRONG = "Something went wrong" + + // Headers + val SERVER: CharSequence = HttpHeaders.createOptimized("Vert.x-Web") + val APPLICATION_JSON: CharSequence = HttpHeaders.createOptimized("application/json") + val TEXT_PLAIN: CharSequence = HttpHeaders.createOptimized("text/plain") + val TEXT_HTML: CharSequence = HttpHeaders.createOptimized("text/html; charset=utf-8") + + inline fun MultiMap.common(): MultiMap = this + .add(HttpHeaders.SERVER, SERVER) + .add(HttpHeaders.DATE, PeriodicDateResolver.current) + + inline fun RoutingContext.json(): HttpServerResponse { + val response = this.response() + val headers = response.headers() + headers + .common() + .add(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) + return response + } + + inline fun RoutingContext.text(): HttpServerResponse { + val response = this.response() + val headers = response.headers() + headers + .common() + .add(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) + return response + } + + inline fun RoutingContext.html(): HttpServerResponse { + val response = this.response() + val headers = response.headers() + headers + .common() + .add(HttpHeaders.CONTENT_TYPE, TEXT_HTML) + return response + } + + inline fun RoutingContext.error(): Unit = this.text().end(SOMETHING_WENT_WRONG, NULL_HANDLER) + } +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/DefaultHandler.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/DefaultHandler.kt new file mode 100644 index 00000000000..8311474177e --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/DefaultHandler.kt @@ -0,0 +1,15 @@ +package com.example.starter.handlers + +import io.vertx.core.buffer.Buffer +import io.vertx.ext.web.RoutingContext + +class DefaultHandler : AbstractHandler() { + fun plaintext(ctx: RoutingContext) { + ctx.text().end(MESSAGE_BUFFER, NULL_HANDLER) + } + + companion object { + private const val MESSAGE = "Hello, World!" + private val MESSAGE_BUFFER = Buffer.buffer(MESSAGE, "UTF-8") + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/FortuneHandler.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/FortuneHandler.kt new file mode 100644 index 00000000000..5e60c04cfeb --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/FortuneHandler.kt @@ -0,0 +1,57 @@ +package com.example.starter.handlers + +import com.example.starter.db.FortuneRepository +import com.example.starter.models.Fortune +import htmlflow.HtmlFlow +import htmlflow.HtmlView +import io.vertx.ext.web.RoutingContext +import org.apache.logging.log4j.kotlin.Logging + +class FortuneHandler(private val repository: FortuneRepository) : AbstractHandler() { + fun templateFortunes(ctx: RoutingContext) { + repository + .selectFortunes() + .onSuccess { + val updatedFortunes = it.plus(Fortune(0, "Additional fortune added at request time.")) + updatedFortunes.sort() + ctx.html().end(TEMPLATE.render(updatedFortunes), NULL_HANDLER) + } + .onFailure { + logger.error(it) { SOMETHING_WENT_WRONG } + ctx.error() + } + } + + companion object : Logging { + private val TEMPLATE: HtmlView = HtmlFlow + .view { page -> + page + .html() + .head() + .title() + .text("Fortunes") + .`__`() // title + .`__`() // head + .body() + .table() + .tr() + .th().text("id").`__`() + .th().text("message").`__`() + .`__`() // tr + .dynamic> { container, fortunes -> + fortunes.forEach { + container + .tr() + .td().text(it.id.toString()).`__`() + .td().text(it.message).`__`() + .`__`() // tr + } + } + .`__`() // table + .`__`() // body + .`__`() // html + } + .setIndented(false) + .threadSafe() + } +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/MessageHandler.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/MessageHandler.kt new file mode 100644 index 00000000000..1fa432ef313 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/MessageHandler.kt @@ -0,0 +1,15 @@ +package com.example.starter.handlers + +import com.example.starter.models.Message +import com.example.starter.utils.serialize +import io.vertx.ext.web.RoutingContext + +class MessageHandler : AbstractHandler() { + fun readDefaultMessage(ctx: RoutingContext) { + ctx.json().end(DEFAULT_MESSAGE.serialize(), NULL_HANDLER) + } + + companion object { + private val DEFAULT_MESSAGE = Message("Hello, World!") + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/WorldHandler.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/WorldHandler.kt new file mode 100644 index 00000000000..1226361d71b --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/WorldHandler.kt @@ -0,0 +1,56 @@ +package com.example.starter.handlers + +import com.example.starter.db.WorldRepository +import com.example.starter.utils.serialize +import io.vertx.ext.web.RoutingContext +import org.apache.logging.log4j.kotlin.Logging + +class WorldHandler(private val repository: WorldRepository) : AbstractHandler() { + fun readRandomWorld(ctx: RoutingContext) { + repository + .selectRandomWorld() + .onSuccess { + ctx.json().end(it.serialize(), NULL_HANDLER) + } + .onFailure { + logger.error(it) { SOMETHING_WENT_WRONG } + ctx.error() + } + } + + fun readRandomWorlds(ctx: RoutingContext) { + val queries = ctx.queries() + repository + .selectRandomWorlds(queries) + .onSuccess { + ctx.json().end(it.serialize(), NULL_HANDLER) + } + .onFailure { + logger.error(it) { SOMETHING_WENT_WRONG } + ctx.error() + } + } + + fun updateRandomWorlds(ctx: RoutingContext) { + val queries = ctx.queries() + repository + .updateRandomWorlds(queries) + .onSuccess { + ctx.json().end(it.serialize(), NULL_HANDLER) + } + .onFailure { + logger.error(it) { SOMETHING_WENT_WRONG } + ctx.error() + } + } + + companion object : Logging { + private const val QUERIES_PARAM_NAME = "queries" + + @Suppress("NOTHING_TO_INLINE") + private inline fun RoutingContext.queries(): Int { + val queriesParam = this.request().getParam(QUERIES_PARAM_NAME) + return queriesParam?.toIntOrNull()?.coerceIn(1, 500) ?: 1 + } + } +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/BufferOutputStream.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/BufferOutputStream.kt new file mode 100644 index 00000000000..d82b31fb6ee --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/BufferOutputStream.kt @@ -0,0 +1,362 @@ +package com.example.starter.io + +import io.netty.buffer.ByteBuf +import io.vertx.core.buffer.Buffer +import io.vertx.core.json.JsonArray +import io.vertx.core.json.JsonObject +import java.io.IOException +import java.io.OutputStream +import java.nio.ByteBuffer +import java.nio.charset.Charset + +class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() { + private val buffer: Buffer = Buffer.buffer(initialSizeHint) + + @Throws(IOException::class) + override fun write(b: Int) { + buffer.appendByte(b.toByte()) + } + + @Throws(IOException::class) + override fun write(bytes: ByteArray) { + buffer.appendBytes(bytes) + } + + @Throws(IOException::class) + override fun write(bytes: ByteArray, offset: Int, len: Int) { + buffer.appendBytes(bytes, offset, len) + } + + override fun toString(enc: String?): String { + return buffer.toString(enc) + } + + override fun toString(enc: Charset?): String { + return buffer.toString(enc) + } + + override fun writeToBuffer(other: Buffer?) { + buffer.writeToBuffer(other) + } + + override fun readFromBuffer(pos: Int, other: Buffer?): Int { + return buffer.readFromBuffer(pos, other) + } + + override fun copy(): Buffer { + return buffer.copy() + } + + override fun toJsonObject(): JsonObject { + return buffer.toJsonObject() + } + + override fun toJsonArray(): JsonArray { + return buffer.toJsonArray() + } + + override fun getByte(pos: Int): Byte { + return buffer.getByte(pos) + } + + override fun getUnsignedByte(pos: Int): Short { + return buffer.getUnsignedByte(pos) + } + + override fun getInt(pos: Int): Int { + return buffer.getInt(pos) + } + + override fun getIntLE(pos: Int): Int { + return buffer.getIntLE(pos) + } + + override fun getUnsignedInt(pos: Int): Long { + return buffer.getUnsignedInt(pos) + } + + override fun getUnsignedIntLE(pos: Int): Long { + return buffer.getUnsignedIntLE(pos) + } + + override fun getLong(pos: Int): Long { + return buffer.getLong(pos) + } + + override fun getLongLE(pos: Int): Long { + return buffer.getLongLE(pos) + } + + override fun getDouble(pos: Int): Double { + return buffer.getDouble(pos) + } + + override fun getFloat(pos: Int): Float { + return buffer.getFloat(pos) + } + + override fun getShort(pos: Int): Short { + return buffer.getShort(pos) + } + + override fun getShortLE(pos: Int): Short { + return buffer.getShortLE(pos) + } + + override fun getUnsignedShort(pos: Int): Int { + return buffer.getUnsignedShort(pos) + } + + override fun getUnsignedShortLE(pos: Int): Int { + return buffer.getUnsignedShortLE(pos) + } + + override fun getMedium(pos: Int): Int { + return buffer.getMedium(pos) + } + + override fun getMediumLE(pos: Int): Int { + return buffer.getMediumLE(pos) + } + + override fun getUnsignedMedium(pos: Int): Int { + return buffer.getUnsignedMedium(pos) + } + + override fun getUnsignedMediumLE(pos: Int): Int { + return buffer.getUnsignedMediumLE(pos) + } + + override fun getBytes(): ByteArray { + return buffer.bytes + } + + override fun getBytes(start: Int, end: Int): ByteArray { + return buffer.getBytes(start, end) + } + + override fun getBytes(dst: ByteArray?): Buffer { + return buffer.getBytes(dst) + } + + override fun getBytes(dst: ByteArray?, dstIndex: Int): Buffer { + return buffer.getBytes(dst, dstIndex) + } + + override fun getBytes(start: Int, end: Int, dst: ByteArray?): Buffer { + return buffer.getBytes(start, end, dst) + } + + override fun getBytes(start: Int, end: Int, dst: ByteArray?, dstIndex: Int): Buffer { + return buffer.getBytes(start, end, dst, dstIndex) + } + + override fun getBuffer(start: Int, end: Int): Buffer { + return buffer.getBuffer(start, end) + } + + override fun getString(start: Int, end: Int, enc: String?): String { + return buffer.getString(start, end, enc) + } + + override fun getString(start: Int, end: Int): String { + return buffer.getString(start, end) + } + + override fun appendBuffer(buff: Buffer?): Buffer { + return buffer.appendBuffer(buff) + } + + override fun appendBuffer(buff: Buffer?, offset: Int, len: Int): Buffer { + return buffer.appendBuffer(buff, offset, len) + } + + override fun appendBytes(bytes: ByteArray?): Buffer { + return buffer.appendBytes(bytes) + } + + override fun appendBytes(bytes: ByteArray?, offset: Int, len: Int): Buffer { + return buffer.appendBytes(bytes, offset, len) + } + + override fun appendByte(b: Byte): Buffer { + return buffer.appendByte(b) + } + + override fun appendUnsignedByte(b: Short): Buffer { + return buffer.appendUnsignedByte(b) + } + + override fun appendInt(i: Int): Buffer { + return buffer.appendInt(i) + } + + override fun appendIntLE(i: Int): Buffer { + return buffer.appendIntLE(i) + } + + override fun appendUnsignedInt(i: Long): Buffer { + return buffer.appendUnsignedInt(i) + } + + override fun appendUnsignedIntLE(i: Long): Buffer { + return buffer.appendUnsignedIntLE(i) + } + + override fun appendMedium(i: Int): Buffer { + return buffer.appendMedium(i) + } + + override fun appendMediumLE(i: Int): Buffer { + return buffer.appendMediumLE(i) + } + + override fun appendLong(l: Long): Buffer { + return buffer.appendLong(l) + } + + override fun appendLongLE(l: Long): Buffer { + return buffer.appendLongLE(l) + } + + override fun appendShort(s: Short): Buffer { + return buffer.appendShort(s) + } + + override fun appendShortLE(s: Short): Buffer { + return buffer.appendShortLE(s) + } + + override fun appendUnsignedShort(s: Int): Buffer { + return buffer.appendUnsignedShort(s) + } + + override fun appendUnsignedShortLE(s: Int): Buffer { + return buffer.appendUnsignedShortLE(s) + } + + override fun appendFloat(f: Float): Buffer { + return buffer.appendFloat(f) + } + + override fun appendDouble(d: Double): Buffer { + return buffer.appendDouble(d) + } + + override fun appendString(str: String?, enc: String?): Buffer { + return buffer.appendString(str, enc) + } + + override fun appendString(str: String?): Buffer { + return buffer.appendString(str) + } + + override fun setByte(pos: Int, b: Byte): Buffer { + return buffer.setByte(pos, b) + } + + override fun setUnsignedByte(pos: Int, b: Short): Buffer { + return buffer.setUnsignedByte(pos, b) + } + + override fun setInt(pos: Int, i: Int): Buffer { + return buffer.setInt(pos, i) + } + + override fun setIntLE(pos: Int, i: Int): Buffer { + return buffer.setIntLE(pos, i) + } + + override fun setUnsignedInt(pos: Int, i: Long): Buffer { + return buffer.setUnsignedInt(pos, i) + } + + override fun setUnsignedIntLE(pos: Int, i: Long): Buffer { + return buffer.setUnsignedIntLE(pos, i) + } + + override fun setMedium(pos: Int, i: Int): Buffer { + return buffer.setMedium(pos, i) + } + + override fun setMediumLE(pos: Int, i: Int): Buffer { + return buffer.setMediumLE(pos, i) + } + + override fun setLong(pos: Int, l: Long): Buffer { + return buffer.setLong(pos, l) + } + + override fun setLongLE(pos: Int, l: Long): Buffer { + return buffer.setLongLE(pos, l) + } + + override fun setDouble(pos: Int, d: Double): Buffer { + return buffer.setDouble(pos, d) + } + + override fun setFloat(pos: Int, f: Float): Buffer { + return buffer.setFloat(pos, f) + } + + override fun setShort(pos: Int, s: Short): Buffer { + return buffer.setShort(pos, s) + } + + override fun setShortLE(pos: Int, s: Short): Buffer { + return buffer.setShortLE(pos, s) + } + + override fun setUnsignedShort(pos: Int, s: Int): Buffer { + return buffer.setUnsignedShort(pos, s) + } + + override fun setUnsignedShortLE(pos: Int, s: Int): Buffer { + return buffer.setUnsignedShortLE(pos, s) + } + + override fun setBuffer(pos: Int, b: Buffer?): Buffer { + return buffer.setBuffer(pos, b) + } + + override fun setBuffer(pos: Int, b: Buffer?, offset: Int, len: Int): Buffer { + return buffer.setBuffer(pos, b, offset, len) + } + + override fun setBytes(pos: Int, b: ByteBuffer?): Buffer { + return buffer.setBytes(pos, b) + } + + override fun setBytes(pos: Int, b: ByteArray?): Buffer { + return buffer.setBytes(pos, b) + } + + override fun setBytes(pos: Int, b: ByteArray?, offset: Int, len: Int): Buffer { + return buffer.setBytes(pos, b, offset, len) + } + + override fun setString(pos: Int, str: String?): Buffer { + return buffer.setString(pos, str) + } + + override fun setString(pos: Int, str: String?, enc: String?): Buffer { + return buffer.setString(pos, str, enc) + } + + override fun length(): Int { + return buffer.length() + } + + override fun slice(): Buffer { + return buffer.slice() + } + + override fun slice(start: Int, end: Int): Buffer { + return buffer.slice(start, end) + } + + @Deprecated("Deprecated in Java") + override fun getByteBuf(): ByteBuf { + return buffer.byteBuf + } +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/JsonResource.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/JsonResource.kt new file mode 100644 index 00000000000..fbf355e66a9 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/JsonResource.kt @@ -0,0 +1,15 @@ +package com.example.starter.io + +import io.vertx.core.json.JsonObject + +object JsonResource { + fun of(resource: String): JsonObject { + val classLoader = ClassLoader.getSystemClassLoader() + classLoader.getResourceAsStream(resource)?.use { input -> + val output = BufferOutputStream() + output.write(input.readAllBytes()) + return output.toJsonObject() + } + throw IllegalStateException("$resource not found") + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Fortune.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Fortune.kt new file mode 100644 index 00000000000..4b832526750 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Fortune.kt @@ -0,0 +1,14 @@ +package com.example.starter.models + +import com.dslplatform.json.CompiledJson +import com.dslplatform.json.JsonAttribute + +@CompiledJson +class Fortune( + @JsonAttribute(nullable = false) val id: Int, + @JsonAttribute(nullable = false) val message: String +) : Comparable { + override fun compareTo(other: Fortune): Int { + return message.compareTo(other.message) + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Message.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Message.kt new file mode 100644 index 00000000000..eb7fc6250a3 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Message.kt @@ -0,0 +1,7 @@ +package com.example.starter.models + +import com.dslplatform.json.CompiledJson +import com.dslplatform.json.JsonAttribute + +@CompiledJson +class Message(@JsonAttribute(nullable = false) val message: String) diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/World.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/World.kt new file mode 100644 index 00000000000..5011a2bb2e0 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/World.kt @@ -0,0 +1,14 @@ +package com.example.starter.models + +import com.dslplatform.json.CompiledJson +import com.dslplatform.json.JsonAttribute + +@CompiledJson +class World( + @JsonAttribute(nullable = false) val id: Int, + @JsonAttribute(nullable = false) var randomNumber: Int +) : Comparable { + override fun compareTo(other: World): Int { + return id.compareTo(other.id) + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/FutureExtensions.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/FutureExtensions.kt new file mode 100644 index 00000000000..03d5a018575 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/FutureExtensions.kt @@ -0,0 +1,14 @@ +package com.example.starter.utils + +import io.vertx.core.CompositeFuture +import io.vertx.core.Future +import java.util.concurrent.TimeUnit +import kotlin.time.Duration + +inline fun CompositeFuture.array(): Array = Array(this.size()) { this.resultAt(it) } + +@Suppress("NOTHING_TO_INLINE") +inline fun > T.block(duration: Duration): R = this + .toCompletionStage() + .toCompletableFuture() + .get(duration.inWholeMilliseconds, TimeUnit.MILLISECONDS) \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/JsonExtensions.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/JsonExtensions.kt new file mode 100644 index 00000000000..264be5a7323 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/JsonExtensions.kt @@ -0,0 +1,19 @@ +package com.example.starter.utils + +import com.dslplatform.json.DslJson +import com.dslplatform.json.runtime.Settings +import com.example.starter.io.BufferOutputStream +import io.vertx.core.buffer.Buffer + +val DSL_JSON: DslJson = DslJson( + Settings.withRuntime() + .includeServiceLoader() + .useStringValuesCache(DslJson.SimpleStringCache()) +) + +@Suppress("NOTHING_TO_INLINE") +inline fun T.serialize(initialSizeHint: Int = 0): Buffer { + val output = BufferOutputStream(initialSizeHint) + DSL_JSON.serialize(this, output) + return output +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/PeriodicDateResolver.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/PeriodicDateResolver.kt new file mode 100644 index 00000000000..23786587569 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/PeriodicDateResolver.kt @@ -0,0 +1,19 @@ +package com.example.starter.utils + +import io.vertx.core.Vertx +import io.vertx.core.http.HttpHeaders +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter + +object PeriodicDateResolver { + var current: CharSequence = next() + + fun init(vertx: Vertx) { + vertx.setPeriodic(1000L) { current = next() } + } + + @Suppress("NOTHING_TO_INLINE") + private inline fun next(): CharSequence = HttpHeaders.createOptimized( + DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now()) + ) +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/RowSetExtensions.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/RowSetExtensions.kt new file mode 100644 index 00000000000..3959ba8d9a0 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/RowSetExtensions.kt @@ -0,0 +1,15 @@ +package com.example.starter.utils + +import io.vertx.sqlclient.Row +import io.vertx.sqlclient.RowSet + +// This extension relies on the assumption the mapper never returns null, as it is defined. Otherwise, +// we prevent the overhead from having to do another iteration over the loop for a `filterNotNull` check. +@Suppress("UNCHECKED_CAST") +inline fun RowSet.mapToArray(mapper: (Row) -> U): Array { + val arr = arrayOfNulls(this.size()) + val iterator = this.iterator() + var index = 0 + while (iterator.hasNext()) arr[index++] = mapper(iterator.next()) + return arr as Array +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/ThrowableExtensions.kt b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/ThrowableExtensions.kt new file mode 100644 index 00000000000..00cf1463297 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/ThrowableExtensions.kt @@ -0,0 +1,16 @@ +package com.example.starter.utils + +import io.netty.channel.unix.Errors +import io.netty.channel.unix.Errors.NativeIoException +import java.net.SocketException + +const val CONNECTION_RESET_MESSAGE = "Connection reset" + +@Suppress("NOTHING_TO_INLINE") +inline fun Throwable.isConnectionReset(): Boolean { + return when { + this is NativeIoException && this.expectedErr() == Errors.ERRNO_ECONNRESET_NEGATIVE -> true + this is SocketException && this.message == CONNECTION_RESET_MESSAGE -> true + else -> false + } +} diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/http-server-options.json b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/http-server-options.json new file mode 100644 index 00000000000..a790bc93043 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/http-server-options.json @@ -0,0 +1,6 @@ +{ + "port": 8080, + "tcpFastOpen": true, + "receiveBufferSize": 262144, + "sendBufferSize": 262144 +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/log4j2.xml b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ebe0ad3cb74 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/log4j2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/pg-connect-options.json b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/pg-connect-options.json new file mode 100644 index 00000000000..3cf43317687 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/pg-connect-options.json @@ -0,0 +1,12 @@ +{ + "user": "benchmarkdbuser", + "password": "benchmarkdbpass", + "host": "tfb-database", + "port": 5432, + "database": "hello_world", + "cachePreparedStatements": true, + "preparedStatementCacheMaxSize": 1024, + "pipeliningLimit": 100000, + "receiveBufferSize": 262144, + "sendBufferSize": 262144 +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson-postgresql.dockerfile b/frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson-postgresql.dockerfile new file mode 100644 index 00000000000..50152d8df30 --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson-postgresql.dockerfile @@ -0,0 +1,39 @@ +FROM gradle:8.9-jdk21 as gradle + +WORKDIR /vertx-web-kotlin-dsljson + +COPY src src +COPY build.gradle.kts build.gradle.kts +COPY gradle.properties gradle.properties +COPY settings.gradle.kts settings.gradle.kts + +RUN gradle shadowJar + +EXPOSE 8080 + +CMD java \ + -server \ + -Xms2G \ + -Xmx2G \ + -XX:+AlwaysPreTouch \ + -XX:+UseParallelGC \ + -XX:InitialCodeCacheSize=512m \ + -XX:ReservedCodeCacheSize=512m \ + -XX:MaxInlineLevel=20 \ + -XX:+UseNUMA \ + -Djava.lang.Integer.IntegerCache.high=10000 \ + -Dvertx.disableMetrics=true \ + -Dvertx.disableH2c=true \ + -Dvertx.disableWebsockets=true \ + -Dvertx.flashPolicyHandler=false \ + -Dvertx.threadChecks=false \ + -Dvertx.disableContextTimings=true \ + -Dvertx.disableTCCL=true \ + -Dvertx.disableHttpHeadersValidation=true \ + -Dvertx.eventLoopPoolSize=$((`grep --count ^processor /proc/cpuinfo`)) \ + -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \ + -Dio.netty.buffer.checkBounds=false \ + -Dio.netty.buffer.checkAccessible=false \ + -Dtfb.hasDB=true \ + -jar \ + build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar diff --git a/frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson.dockerfile b/frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson.dockerfile new file mode 100644 index 00000000000..568f19f79dd --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson.dockerfile @@ -0,0 +1,39 @@ +FROM gradle:8.9-jdk21 as gradle + +WORKDIR /vertx-web-kotlin-dsljson + +COPY src src +COPY build.gradle.kts build.gradle.kts +COPY gradle.properties gradle.properties +COPY settings.gradle.kts settings.gradle.kts + +RUN gradle shadowJar + +EXPOSE 8080 + +CMD java \ + -server \ + -Xms2G \ + -Xmx2G \ + -XX:+AlwaysPreTouch \ + -XX:+UseParallelGC \ + -XX:InitialCodeCacheSize=512m \ + -XX:ReservedCodeCacheSize=512m \ + -XX:MaxInlineLevel=20 \ + -XX:+UseNUMA \ + -Djava.lang.Integer.IntegerCache.high=10000 \ + -Dvertx.disableMetrics=true \ + -Dvertx.disableH2c=true \ + -Dvertx.disableWebsockets=true \ + -Dvertx.flashPolicyHandler=false \ + -Dvertx.threadChecks=false \ + -Dvertx.disableContextTimings=true \ + -Dvertx.disableTCCL=true \ + -Dvertx.disableHttpHeadersValidation=true \ + -Dvertx.eventLoopPoolSize=$((`grep --count ^processor /proc/cpuinfo`)) \ + -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \ + -Dio.netty.buffer.checkBounds=false \ + -Dio.netty.buffer.checkAccessible=false \ + -Dtfb.hasDB=false \ + -jar \ + build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar 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/OCaml/dream/README.md b/frameworks/OCaml/dream/README.md new file mode 100755 index 00000000000..86e484bc978 --- /dev/null +++ b/frameworks/OCaml/dream/README.md @@ -0,0 +1,35 @@ +# Dream + +## Overview + +Most of all of the code is inside of `test_dream/bin/main.ml` file. + +## Implemented tests + +| Test Name | Endpoint | +|------------|-------------------------------| +| Plain text | http://0.0.0.0:8080/plaintext | +| Json | http://0.0.0.0:8080/json | + +## Headers + +A simple middleware was added that adds the required headers for the Techempower Benchmarks. +The date header is refreshed only once per second as allowed by the rules for performance. + +## Dependencies + +The `test_dream/dune-project` and `test_dream/bin/dune` are where dependencies are managed +for this project. If you add a dependency to those locations and then run the following: + +``` +cd test_dream +dune build test_dream.opam +opam install --yes --deps-only . +``` + +You will update the opam package list and install your changes locally. + +## Running tests + +$ tfb --mode verify --test dream + diff --git a/frameworks/OCaml/dream/benchmark_config.json b/frameworks/OCaml/dream/benchmark_config.json new file mode 100755 index 00000000000..d5d83b838f4 --- /dev/null +++ b/frameworks/OCaml/dream/benchmark_config.json @@ -0,0 +1,26 @@ +{ + "framework": "dream", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "postgres", + "framework": "Dream", + "language": "OCaml", + "flavor": "None", + "orm": "Micro", + "platform": "http/af", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Dream", + "notes": "", + "versus": "httpaf" + } + } + ] +} diff --git a/frameworks/OCaml/dream/dream.dockerfile b/frameworks/OCaml/dream/dream.dockerfile new file mode 100644 index 00000000000..8a806026a4e --- /dev/null +++ b/frameworks/OCaml/dream/dream.dockerfile @@ -0,0 +1,12 @@ +FROM ocaml/opam:debian-ocaml-5.1 +COPY ./dream_test/ /app +WORKDIR /app +USER root +RUN apt install -y libgmp-dev libev-dev pkg-config libssl-dev +RUN opam install --yes --deps-only . +RUN opam install dune +RUN eval $(opam env) +RUN opam exec -- dune build --profile release +CMD [ "./_build/default/bin/main.exe" ] +EXPOSE 8080 + diff --git a/frameworks/OCaml/dream/dream_test/bin/dune b/frameworks/OCaml/dream/dream_test/bin/dune new file mode 100755 index 00000000000..171e6829182 --- /dev/null +++ b/frameworks/OCaml/dream/dream_test/bin/dune @@ -0,0 +1,5 @@ +(executable + (public_name dream_test) + (name main) + (preprocess (pps lwt_ppx ppx_yojson_conv)) + (libraries dream_test dream ppx_yojson_conv calendar)) diff --git a/frameworks/OCaml/dream/dream_test/bin/main.ml b/frameworks/OCaml/dream/dream_test/bin/main.ml new file mode 100755 index 00000000000..4c5ac18c30c --- /dev/null +++ b/frameworks/OCaml/dream/dream_test/bin/main.ml @@ -0,0 +1,53 @@ +open Ppx_yojson_conv_lib.Yojson_conv.Primitives + +type message_object = { + message : string; +} [@@deriving yojson] + +let time_cache = Atomic.make false;; +let time_ref = ref("None");; + +let time () = + if not @@ Atomic.get time_cache + then begin + time_ref := CalendarLib.Printer.Calendar.sprint "%a, %d %b %Y %H:%M:%S UTC" @@ CalendarLib.Calendar.now (); + Atomic.set time_cache true; + (!time_ref) + end + else + (!time_ref);; + +let tech_empower_headers (inner_handler: Dream.handler) = + (fun (req) -> + let%lwt res = inner_handler req in + Dream.set_header res "Server" "dream"; + Dream.set_header res "Date" @@ time (); + Lwt.return res + );; + +let rec timer () = + Unix.sleepf 0.9; + Atomic.set time_cache false; + timer();; + +let () = + let time_invalidator = Domain.spawn(fun () -> timer ()) in + Dream.run ~interface: "0.0.0.0" + @@ tech_empower_headers + @@ Dream.router [ + Dream.get "/" (fun _ -> + Dream.html "Hello, world!" + ); + Dream.get "/plaintext" (fun _ -> + Dream.response ~headers: [("Content-Type", "text/plain")] + "Hello, world!" + |> Lwt.return + ); + Dream.get "/json" (fun _ -> + { message = "Hello, world!" } + |> yojson_of_message_object + |> Yojson.Safe.to_string + |> Dream.json + ); + ]; + Domain.join time_invalidator;; diff --git a/frameworks/OCaml/dream/dream_test/dream_test.opam b/frameworks/OCaml/dream/dream_test/dream_test.opam new file mode 100644 index 00000000000..b9db80a34bd --- /dev/null +++ b/frameworks/OCaml/dream/dream_test/dream_test.opam @@ -0,0 +1,35 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "A short synopsis" +description: "A longer description" +maintainer: ["Maintainer Name"] +authors: ["Author Name"] +license: "LICENSE" +tags: ["topics" "to describe" "your" "project"] +homepage: "https://github.com/username/reponame" +doc: "https://url/to/documentation" +bug-reports: "https://github.com/username/reponame/issues" +depends: [ + "ocaml" + "dune" {>= "3.14"} + "dream" + "lwt" + "ppx_yojson_conv" + "calendar" + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/username/reponame.git" diff --git a/frameworks/OCaml/dream/dream_test/dune-project b/frameworks/OCaml/dream/dream_test/dune-project new file mode 100755 index 00000000000..aaeaf8c0df8 --- /dev/null +++ b/frameworks/OCaml/dream/dream_test/dune-project @@ -0,0 +1,26 @@ +(lang dune 3.14) + +(name dream_test) + +(generate_opam_files true) + +(source + (github username/reponame)) + +(authors "Author Name") + +(maintainers "Maintainer Name") + +(license LICENSE) + +(documentation https://url/to/documentation) + +(package + (name dream_test) + (synopsis "A short synopsis") + (description "A longer description") + (depends ocaml dune dream lwt ppx_yojson_conv calendar) + (tags + (topics "to describe" your project))) + +; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project diff --git a/frameworks/OCaml/dream/dream_test/lib/dune b/frameworks/OCaml/dream/dream_test/lib/dune new file mode 100755 index 00000000000..b8d991f91da --- /dev/null +++ b/frameworks/OCaml/dream/dream_test/lib/dune @@ -0,0 +1,2 @@ +(library + (name dream_test)) diff --git a/frameworks/OCaml/dream/dream_test/test/dune b/frameworks/OCaml/dream/dream_test/test/dune new file mode 100755 index 00000000000..434f4a8ee36 --- /dev/null +++ b/frameworks/OCaml/dream/dream_test/test/dune @@ -0,0 +1,2 @@ +(test + (name test_dream_test)) diff --git a/frameworks/OCaml/dream/dream_test/test/test_dream_test.ml b/frameworks/OCaml/dream/dream_test/test/test_dream_test.ml new file mode 100755 index 00000000000..e69de29bb2d diff --git a/frameworks/PHP/laravel/benchmark_config.json b/frameworks/PHP/laravel/benchmark_config.json index 588dab1e17f..053534b12fa 100644 --- a/frameworks/PHP/laravel/benchmark_config.json +++ b/frameworks/PHP/laravel/benchmark_config.json @@ -138,6 +138,29 @@ "display_name": "laravel-octane [frankenphp]", "notes": "", "versus": "php" + }, + "ripple": { + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries/", + "fortune_url": "/fortunes", + "update_url": "/updates/", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MySQL", + "framework": "laravel", + "language": "PHP", + "flavor": "PHP8.3", + "orm": "Full", + "platform": "Ripple", + "webserver": "PServer", + "os": "Linux", + "database_os": "Linux", + "display_name": "laravel-ripple", + "notes": "", + "versus": "php" } }] -} \ No newline at end of file +} diff --git a/frameworks/PHP/laravel/composer.json b/frameworks/PHP/laravel/composer.json index 094026428e5..f25952eccea 100644 --- a/frameworks/PHP/laravel/composer.json +++ b/frameworks/PHP/laravel/composer.json @@ -8,7 +8,7 @@ ], "license": "MIT", "require": { - "laravel/framework": "^10" + "laravel/framework": "^11" }, "config": { "optimize-autoloader": true, diff --git a/frameworks/PHP/laravel/deploy/conf/php.ini b/frameworks/PHP/laravel/deploy/conf/php.ini index 82133535145..98e34225e0d 100644 --- a/frameworks/PHP/laravel/deploy/conf/php.ini +++ b/frameworks/PHP/laravel/deploy/conf/php.ini @@ -1767,7 +1767,7 @@ ldap.max_links = -1 opcache.enable=1 ; Determines if Zend OPCache is enabled for the CLI version of PHP -;opcache.enable_cli=0 +opcache.enable_cli=1 ; The OPcache shared memory storage size. ;opcache.memory_consumption=128 diff --git a/frameworks/PHP/laravel/deploy/workerman/composer.json b/frameworks/PHP/laravel/deploy/workerman/composer.json deleted file mode 100644 index 343ff8fcf3c..00000000000 --- a/frameworks/PHP/laravel/deploy/workerman/composer.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "laravel/laravel", - "type": "project", - "description": "The Laravel Framework.", - "keywords": [ - "framework", - "laravel" - ], - "license": "MIT", - "require": { - "php": "^8.0", - "laravel/framework": "^8.0", - "joanhey/adapterman": "^0.6" - }, - "require-dev": { - "facade/ignition": "^2.3.6", - "fzaninotto/faker": "^1.9.1", - "mockery/mockery": "^1.3.1", - "nunomaduro/collision": "^5.0", - "phpunit/phpunit": "^9.3" - }, - "config": { - "optimize-autoloader": true, - "preferred-install": "dist", - "sort-packages": true - }, - "extra": { - "laravel": { - "dont-discover": [] - } - }, - "autoload": { - "psr-4": { - "App\\": "app/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } - }, - "minimum-stability": "dev", - "prefer-stable": true, - "scripts": { - "post-autoload-dump": [ - "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", - "@php artisan package:discover --ansi" - ], - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "post-create-project-cmd": [ - "@php artisan key:generate --ansi" - ] - } -} - - diff --git a/frameworks/PHP/laravel/laravel-laravel-s.dockerfile b/frameworks/PHP/laravel/laravel-laravel-s.dockerfile index 4cce4090f3d..c353ef28b4f 100644 --- a/frameworks/PHP/laravel/laravel-laravel-s.dockerfile +++ b/frameworks/PHP/laravel/laravel-laravel-s.dockerfile @@ -1,25 +1,21 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole -RUN docker-php-ext-install pdo_mysql pcntl opcache > /dev/null +RUN docker-php-ext-install pcntl opcache curl > /dev/null RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini RUN echo "opcache.jit=1205" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini RUN echo "opcache.jit_buffer_size=128M" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini -ADD ./ /laravel WORKDIR /laravel +COPY --link . . -RUN mkdir -p /laravel/bootstrap/cache /laravel/storage/logs /laravel/storage/framework/sessions /laravel/storage/framework/views /laravel/storage/framework/cache -RUN chmod -R 777 /laravel +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache -RUN apt-get update > /dev/null && \ - apt-get install -yqq git unzip > /dev/null -RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && php composer-setup.php && php -r "unlink('composer-setup.php');" -RUN mv composer.phar /usr/local/bin/composer - -COPY deploy/laravel-s/composer.json ./ +COPY --link deploy/laravel-s/composer.json . RUN echo "LARAVELS_LISTEN_IP=0.0.0.0" >> .env RUN echo "LARAVELS_LISTEN_PORT=8080" >> .env @@ -30,4 +26,4 @@ RUN php artisan laravels publish EXPOSE 8080 -CMD php bin/laravels start +ENTRYPOINT [ "php", "bin/laravels", "start" ] diff --git a/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile b/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile index 1829a4292f4..2ad30a612e3 100644 --- a/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile +++ b/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile @@ -1,25 +1,25 @@ FROM dunglas/frankenphp RUN install-php-extensions \ - pcntl \ + intl \ + opcache \ + pcntl \ pdo_mysql \ - intl \ - zip \ - opcache > /dev/null + zip > /dev/null -COPY . /app +COPY --link . /app/ -COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer +COPY --from=composer --link /usr/bin/composer /usr/local/bin/composer -RUN mkdir -p /app/bootstrap/cache /app/storage/logs /app/storage/framework/sessions /app/storage/framework/views /app/storage/framework/cache -RUN chmod -R 777 /app +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache -COPY deploy/conf/php.ini /usr/local/etc/php - -RUN composer require laravel/octane guzzlehttp/guzzle - -RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY --link deploy/conf/php.ini /usr/local/etc/php +RUN composer require laravel/octane guzzlehttp/guzzle --update-no-dev --no-scripts --quiet RUN php artisan optimize RUN frankenphp -v diff --git a/frameworks/PHP/laravel/laravel-ripple.dockerfile b/frameworks/PHP/laravel/laravel-ripple.dockerfile new file mode 100644 index 00000000000..4f1bb8bc3a9 --- /dev/null +++ b/frameworks/PHP/laravel/laravel-ripple.dockerfile @@ -0,0 +1,49 @@ +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 pdo_mysql opcache posix pcntl sockets +RUN docker-php-ext-enable --ini-name zz-event.ini event +RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini +RUN echo "opcache.jit=1205" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini +RUN echo "opcache.jit_buffer_size=128M" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini + +COPY --from=composer --link /usr/bin/composer /usr/local/bin/composer + +# Initialize +WORKDIR /laravel +COPY --link . . + +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache + +RUN echo "PRP_HTTP_LISTEN=http://0.0.0.0:8080" >> .env +RUN echo "PRP_HTTP_WORKERS=64" >> .env +RUN echo "PRP_HTTP_RELOAD=0" >> .env +RUN echo "PRP_HTTP_SANDBOX=1" >> .env + +# Configure +RUN composer install --quiet +RUN composer require cloudtay/ripple-driver --quiet +RUN php artisan vendor:publish --tag=ripple-config +RUN php artisan optimize + +# Start +EXPOSE 8080 +ENTRYPOINT ["php","artisan","ripple:server","start"] diff --git a/frameworks/PHP/laravel/laravel-roadrunner.dockerfile b/frameworks/PHP/laravel/laravel-roadrunner.dockerfile index d5d42d62879..33c7067c7b0 100644 --- a/frameworks/PHP/laravel/laravel-roadrunner.dockerfile +++ b/frameworks/PHP/laravel/laravel-roadrunner.dockerfile @@ -9,7 +9,11 @@ RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opca WORKDIR /laravel COPY --link . . -RUN mkdir -p /laravel/bootstrap/cache /laravel/storage/logs /laravel/storage/framework/sessions /laravel/storage/framework/views /laravel/storage/framework/cache +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache RUN apt-get update > /dev/null && \ apt-get install -yqq curl unzip > /dev/null diff --git a/frameworks/PHP/laravel/laravel-swoole.dockerfile b/frameworks/PHP/laravel/laravel-swoole.dockerfile index 77cfef19fe0..52308a4dd5e 100644 --- a/frameworks/PHP/laravel/laravel-swoole.dockerfile +++ b/frameworks/PHP/laravel/laravel-swoole.dockerfile @@ -1,33 +1,27 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole -RUN docker-php-ext-install pdo_mysql pcntl opcache > /dev/null +RUN docker-php-ext-install pcntl opcache curl > /dev/null RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini RUN echo "opcache.jit=1205" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini RUN echo "opcache.jit_buffer_size=128M" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini -ADD ./ /laravel WORKDIR /laravel +COPY --link . . -RUN mkdir -p /laravel/bootstrap/cache /laravel/storage/framework/sessions /laravel/storage/framework/views /laravel/storage/framework/cache -RUN chmod -R 777 /laravel +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache -RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq git unzip > /dev/null - -RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && php composer-setup.php && php -r "unlink('composer-setup.php');" -RUN mv composer.phar /usr/local/bin/composer - -COPY deploy/swoole/composer.json ./ +COPY --link deploy/swoole/composer.json . RUN echo "APP_SWOOLE=true" >> .env RUN composer install -a --no-dev --quiet RUN php artisan optimize - EXPOSE 8080 -CMD php artisan swoole:http start +ENTRYPOINT [ "php", "artisan", "swoole:http", "start" ] diff --git a/frameworks/PHP/laravel/laravel-workerman.dockerfile b/frameworks/PHP/laravel/laravel-workerman.dockerfile index 48cf4ee5e42..34ee2de30a5 100644 --- a/frameworks/PHP/laravel/laravel-workerman.dockerfile +++ b/frameworks/PHP/laravel/laravel-workerman.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -9,23 +9,25 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ RUN apt-get install -yqq git unzip \ php8.3-cli php8.3-mysql php8.3-mbstring php8.3-xml php8.3-curl > /dev/null -COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer +COPY --from=composer --link /usr/bin/composer /usr/local/bin/composer RUN apt-get install -y php-pear php8.3-dev libevent-dev > /dev/null RUN pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini -ADD ./ /laravel WORKDIR /laravel +COPY --link . . -EXPOSE 8080 - -RUN mkdir -p /laravel/bootstrap/cache /laravel/storage/logs /laravel/storage/framework/sessions /laravel/storage/framework/views /laravel/storage/framework/cache -RUN chmod -R 777 /laravel +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache -COPY deploy/workerman/composer.json ./ -RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +RUN composer require joanhey/adapterman --update-no-dev --no-scripts --quiet RUN php artisan optimize -COPY deploy/conf/cli-php.ini /etc/php/8.3/cli/php.ini +COPY --link deploy/conf/cli-php.ini /etc/php/8.3/cli/php.ini + +EXPOSE 8080 -CMD php server-man.php start +ENTRYPOINT [ "php", "server-man.php", "start" ] diff --git a/frameworks/PHP/laravel/laravel.dockerfile b/frameworks/PHP/laravel/laravel.dockerfile index c1858216a66..455d2e6d60f 100644 --- a/frameworks/PHP/laravel/laravel.dockerfile +++ b/frameworks/PHP/laravel/laravel.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -7,19 +7,21 @@ 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-cli php8.3-fpm php8.3-mysql php8.3-mbstring php8.3-xml php8.3-dev > /dev/null + php8.3-cli php8.3-fpm php8.3-mysql php8.3-mbstring php8.3-xml php8.3-curl > /dev/null -COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer +COPY --from=composer --link /usr/bin/composer /usr/local/bin/composer -COPY deploy/conf/* /etc/php/8.3/fpm/ - -ADD ./ /laravel +COPY --link deploy/conf/* /etc/php/8.3/fpm/ WORKDIR /laravel +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 mkdir -p /laravel/bootstrap/cache /laravel/storage/logs /laravel/storage/framework/sessions /laravel/storage/framework/views /laravel/storage/framework/cache -RUN chmod -R 777 /laravel +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet RUN php artisan optimize @@ -29,6 +31,5 @@ EXPOSE 8080 # Uncomment next line for Laravel console error logging to be viewable in docker logs # RUN echo "catch_workers_output = yes" >> /etc/php/8.3/fpm/php-fpm.conf -RUN mkdir -p /run/php -CMD /usr/sbin/php-fpm8.3 --fpm-config /etc/php/8.3/fpm/php-fpm.conf && \ +CMD service php8.3-fpm start && \ nginx -c /laravel/deploy/nginx.conf diff --git a/frameworks/PHP/leaf/leaf-workerman.dockerfile b/frameworks/PHP/leaf/leaf-workerman.dockerfile index eaa11589d3a..ddcc5dc928c 100644 --- a/frameworks/PHP/leaf/leaf-workerman.dockerfile +++ b/frameworks/PHP/leaf/leaf-workerman.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -7,21 +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 update -yqq > /dev/null && apt-get install -yqq git \ - php8.3-cli php8.3-mysql php8.3-mbstring php8.3-xml php8.3-curl > /dev/null + php8.3-cli php8.3-mysql php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip > /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 > /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 libevent-dev php8.3-dev > /dev/null \ + && pecl install event-3.1.4 > /dev/null \ + && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini -COPY deploy/conf/cli-php.ini /etc/php/8.3/cli/php.ini +COPY --link deploy/conf/cli-php.ini /etc/php/8.3/cli/php.ini -ADD ./ /leaf WORKDIR /leaf +COPY --link . . EXPOSE 8080 -RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet RUN composer require joanhey/adapterman:^0.6 --quiet diff --git a/frameworks/PHP/leaf/leaf.dockerfile b/frameworks/PHP/leaf/leaf.dockerfile index 167a5af7039..52a4c2f704c 100644 --- a/frameworks/PHP/leaf/leaf.dockerfile +++ b/frameworks/PHP/leaf/leaf.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -7,14 +7,14 @@ 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 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql php8.3-xml php8.3-curl > /dev/null + php8.3-cli php8.3-fpm php8.3-mysql php8.3-xml php8.3-curl php8.3-zip > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY --link deploy/conf/* /etc/php/8.3/fpm/ -ADD ./ /leaf WORKDIR /leaf +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; diff --git a/frameworks/PHP/lumen/composer.json b/frameworks/PHP/lumen/composer.json index aaf2e3d44d1..2552d9450e3 100644 --- a/frameworks/PHP/lumen/composer.json +++ b/frameworks/PHP/lumen/composer.json @@ -5,7 +5,7 @@ "license": "MIT", "type": "project", "require": { - "laravel/lumen-framework": "^10" + "laravel/lumen-framework": "^11" }, "autoload": { "psr-4": { diff --git a/frameworks/PHP/lumen/lumen-laravel-s.dockerfile b/frameworks/PHP/lumen/lumen-laravel-s.dockerfile index a8a0aebd68a..9c2df7afaef 100644 --- a/frameworks/PHP/lumen/lumen-laravel-s.dockerfile +++ b/frameworks/PHP/lumen/lumen-laravel-s.dockerfile @@ -1,23 +1,21 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole -RUN docker-php-ext-install pdo_mysql pcntl opcache > /dev/null +RUN docker-php-ext-install pcntl opcache curl > /dev/null RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini #RUN echo "opcache.jit=1205" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini #RUN echo "opcache.jit_buffer_size=128M" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini -ADD ./ /lumen WORKDIR /lumen +COPY --link . . -RUN mkdir -p /lumen/bootstrap/cache /lumen/storage/logs /lumen/storage/framework/sessions /lumen/storage/framework/views /lumen/storage/framework/cache -RUN chmod -R 777 /lumen +RUN mkdir -p bootstrap/cache \ + storage/logs \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache -RUN apt-get update > /dev/null && \ - apt-get install -yqq git unzip > /dev/null -RUN php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');" && php composer-setup.php && php -r "unlink('composer-setup.php');" -RUN mv composer.phar /usr/local/bin/composer +RUN chmod -R 777 /lumen COPY deploy/laravel-s/composer.json ./ @@ -29,4 +27,4 @@ RUN php artisan laravels publish EXPOSE 8080 -CMD php bin/laravels start +ENTRYPOINT [ "php", "bin/laravels", "start" ] diff --git a/frameworks/PHP/lumen/lumen-swoole.dockerfile b/frameworks/PHP/lumen/lumen-swoole.dockerfile index 3e4c866c031..0f4f3434e37 100644 --- a/frameworks/PHP/lumen/lumen-swoole.dockerfile +++ b/frameworks/PHP/lumen/lumen-swoole.dockerfile @@ -1,31 +1,16 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole +RUN docker-php-ext-install pcntl opcache curl > /dev/null -RUN docker-php-ext-install pdo_mysql > /dev/null - -ADD ./ /lumen WORKDIR /lumen -COPY deploy/swoole/php.ini /usr/local/etc/php/ - -RUN mkdir -p /lumen/storage/framework/sessions -RUN mkdir -p /lumen/storage/framework/views -RUN mkdir -p /lumen/storage/framework/cache - -RUN chmod -R 777 /lumen +COPY --link . . -# Install composer using the installation method documented at https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md -# This method was chosen because composer is not part of the apt repositories that are in the default PHP 7.2 docker image -# Adding alternate apt php repos can potentially cause problems with extension compatibility between the php build from the docker image and the alternate php build -# An additional benefit of this method is that the correct version of composer will be used for the environment and version of the php system in the docker image -RUN deploy/swoole/install-composer.sh +COPY --link deploy/swoole/php.ini /usr/local/etc/php/ -RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq git unzip > /dev/null +RUN mkdir -p /lumen/storage/framework/sessions /lumen/storage/framework/views /lumen/storage/framework/cache COPY deploy/swoole/composer* ./ -RUN php composer.phar install --optimize-autoloader --classmap-authoritative --no-dev --quiet +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet RUN echo "APP_SWOOLE=true" >> .env @@ -33,4 +18,4 @@ RUN chmod -R 777 /lumen EXPOSE 8080 -CMD php artisan swoole:http start +ENTRYPOINT [ "php", "artisan", "swoole:http", "start" ] diff --git a/frameworks/PHP/lumen/lumen-workerman.dockerfile b/frameworks/PHP/lumen/lumen-workerman.dockerfile index aabe520cdc8..decfe424411 100644 --- a/frameworks/PHP/lumen/lumen-workerman.dockerfile +++ b/frameworks/PHP/lumen/lumen-workerman.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -14,16 +14,17 @@ COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer RUN apt-get install -y php-pear php8.3-dev libevent-dev > /dev/null RUN pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini -ADD ./ /lumen WORKDIR /lumen +COPY --link . . + RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet RUN composer require joanhey/adapterman:^0.6 --quiet -RUN mkdir -p /lumen/storage -RUN mkdir -p /lumen/storage/framework/sessions -RUN mkdir -p /lumen/storage/framework/views -RUN mkdir -p /lumen/storage/framework/cache +RUN mkdir -p storage \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache RUN chmod -R 777 /lumen diff --git a/frameworks/PHP/lumen/lumen.dockerfile b/frameworks/PHP/lumen/lumen.dockerfile index 1b5f8a76e62..9d11da427af 100644 --- a/frameworks/PHP/lumen/lumen.dockerfile +++ b/frameworks/PHP/lumen/lumen.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -13,17 +13,18 @@ COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer COPY deploy/conf/* /etc/php/8.3/fpm/ -ADD ./ /lumen WORKDIR /lumen +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 composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -RUN mkdir -p /lumen/storage -RUN mkdir -p /lumen/storage/framework/sessions -RUN mkdir -p /lumen/storage/framework/views -RUN mkdir -p /lumen/storage/framework/cache +RUN mkdir -p storage \ + storage/framework/sessions \ + storage/framework/views \ + storage/framework/cache RUN chmod -R 777 /lumen diff --git a/frameworks/PHP/mixphp/mixphp-swoole-mysql.dockerfile b/frameworks/PHP/mixphp/mixphp-swoole-mysql.dockerfile index 2001b9fde1d..846a3b88a87 100644 --- a/frameworks/PHP/mixphp/mixphp-swoole-mysql.dockerfile +++ b/frameworks/PHP/mixphp/mixphp-swoole-mysql.dockerfile @@ -1,13 +1,10 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN pecl install swoole > /dev/null && docker-php-ext-enable swoole +RUN docker-php-ext-install pcntl opcache bcmath > /dev/null -RUN docker-php-ext-install opcache pdo_mysql bcmath > /dev/null - -RUN apt -yqq update && apt -yqq install git unzip > /dev/null - -COPY . /mixphp -COPY php.ini /usr/local/etc/php/ +WORKDIR /mixphp +COPY --link . . +COPY --link php.ini /usr/local/etc/php/ RUN echo "opcache.enable=1" >> /usr/local/etc/php/php.ini RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/php.ini RUN echo "pcre.jit=1" >> /usr/local/etc/php/php.ini @@ -16,9 +13,6 @@ RUN echo "opcache.jit_buffer_size=256M" >> /usr/local/etc/php/php.ini RUN php -v && php -i | grep opcache -WORKDIR /mixphp - -COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer RUN composer install --no-dev --classmap-authoritative --quiet > /dev/null RUN composer dumpautoload -o @@ -27,4 +21,4 @@ RUN chmod -R 777 /mixphp/runtime/logs EXPOSE 9501 -CMD php /mixphp/bin/swoole.php start +ENTRYPOINT [ "php", "/mixphp/bin/swoole.php", "start" ] diff --git a/frameworks/PHP/php/php-h2o.dockerfile b/frameworks/PHP/php/php-h2o.dockerfile index 57571b6b3c7..3de194e526b 100644 --- a/frameworks/PHP/php/php-h2o.dockerfile +++ b/frameworks/PHP/php/php-h2o.dockerfile @@ -4,7 +4,7 @@ ARG H2O_PREFIX=/opt/h2o FROM "ubuntu:${UBUNTU_VERSION}" AS compile -ARG H2O_VERSION=18b175f71ede08b50d3e5ae8303dacef3ea510fc +ARG H2O_VERSION=c54c63285b52421da2782f028022647fc2ea3dd1 ARG DEBIAN_FRONTEND=noninteractive ARG H2O_PREFIX diff --git a/frameworks/PHP/simps/simps-micro.dockerfile b/frameworks/PHP/simps/simps-micro.dockerfile index baa74e103e1..4e5b9ffd075 100644 --- a/frameworks/PHP/simps/simps-micro.dockerfile +++ b/frameworks/PHP/simps/simps-micro.dockerfile @@ -1,22 +1,15 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole - -RUN docker-php-ext-install opcache pdo_mysql > /dev/null - -RUN apt -yqq update > /dev/null && \ - apt -yqq install git unzip > /dev/null +RUN docker-php-ext-install pcntl opcache curl > /dev/null WORKDIR /simps -COPY . /simps -COPY php.ini /usr/local/etc/php/ +COPY --link . . +COPY --link php.ini /usr/local/etc/php/ -RUN curl -sSL https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN composer install --no-dev --classmap-authoritative --quiet > /dev/null RUN composer dumpautoload -o EXPOSE 8080 -CMD php sbin/simps.php http:start +ENTRYPOINT [ "php", "sbin/simps.php", "http:start" ] diff --git a/frameworks/PHP/simps/simps.dockerfile b/frameworks/PHP/simps/simps.dockerfile index baa74e103e1..4e5b9ffd075 100644 --- a/frameworks/PHP/simps/simps.dockerfile +++ b/frameworks/PHP/simps/simps.dockerfile @@ -1,22 +1,15 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole - -RUN docker-php-ext-install opcache pdo_mysql > /dev/null - -RUN apt -yqq update > /dev/null && \ - apt -yqq install git unzip > /dev/null +RUN docker-php-ext-install pcntl opcache curl > /dev/null WORKDIR /simps -COPY . /simps -COPY php.ini /usr/local/etc/php/ +COPY --link . . +COPY --link php.ini /usr/local/etc/php/ -RUN curl -sSL https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN composer install --no-dev --classmap-authoritative --quiet > /dev/null RUN composer dumpautoload -o EXPOSE 8080 -CMD php sbin/simps.php http:start +ENTRYPOINT [ "php", "sbin/simps.php", "http:start" ] diff --git a/frameworks/PHP/swoole/php.ini b/frameworks/PHP/swoole/10-opcache.ini similarity index 67% rename from frameworks/PHP/swoole/php.ini rename to frameworks/PHP/swoole/10-opcache.ini index 99a548fe57e..bc77a43761e 100644 --- a/frameworks/PHP/swoole/php.ini +++ b/frameworks/PHP/swoole/10-opcache.ini @@ -1,9 +1,10 @@ +zend_extension=opcache.so opcache.enable=1 opcache.enable_cli=1 opcache.validate_timestamps=0 +opcache.save_comments=0 opcache.enable_file_override=1 opcache.huge_code_pages=1 -memory_limit=1024M - opcache.jit_buffer_size=128M +mysqlnd.collect_statistics = Off opcache.jit=tracing diff --git a/frameworks/PHP/swoole/database.php b/frameworks/PHP/swoole/database.php index 6a7cdb3c1ea..a0c15234921 100644 --- a/frameworks/PHP/swoole/database.php +++ b/frameworks/PHP/swoole/database.php @@ -15,20 +15,20 @@ class Operation public static function db(PDOStatement|PDOStatementProxy $db): string { $db->execute([mt_rand(1, 10000)]); - return json_encode($db->fetch(PDO::FETCH_ASSOC), JSON_NUMERIC_CHECK); + return json_encode($db->fetch(PDO::FETCH_ASSOC)); } public static function fortunes(PDOStatement|PDOStatementProxy $fortune): string { $fortune->execute(); - $results = $fortune->fetchAll(PDO::FETCH_KEY_PAIR); + $results = $fortune->fetchAll(PDO::FETCH_KEY_PAIR); $results[0] = 'Additional fortune added at request time.'; asort($results); $html = ''; foreach ($results as $id => $message) { $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); - $html .= "$id$message"; + $html .= "$id$message"; } return "Fortunes$html
idmessage
"; @@ -36,47 +36,47 @@ public static function fortunes(PDOStatement|PDOStatementProxy $fortune): string public static function query(PDOStatement|PDOStatementProxy $query, int $queries): string { - $query_count = 1; - if ($queries > 1) { - $query_count = min($queries, 500); - } - $results = []; - while ($query_count--) { + while ($queries--) { $query->execute([mt_rand(1, 10000)]); $results[] = $query->fetch(PDO::FETCH_ASSOC); } - return json_encode($results, JSON_NUMERIC_CHECK); + return json_encode($results); } - public static function updates(PDOStatement|PDOStatementProxy $random, PDOStatement|PDOStatementProxy $update, int $queries): string + public static function updates(PDOStatement|PDOStatementProxy $random, PDOStatement|PDOStatementProxy $update, int $queries, string $driver): string { - $query_count = 1; - if ($queries > 1) { - $query_count = min($queries, 500); - } + $results = $keys = $values = []; + while ($queries--) { + $random->execute([mt_rand(1, 10000)]); + $item = $random->fetch(PDO::FETCH_ASSOC); + $item['randomNumber'] = mt_rand(1, 10000); + $results[] = $item; - $results = []; - while ($query_count--) { - $id = mt_rand(1, 10000); - $random->execute([$id]); - $item = $random->fetch(PDO::FETCH_ASSOC); - $update->execute([$item['randomNumber'] = mt_rand(1, 10000), $id]); - - $results[] = $item; + if ($driver == 'pgsql') { + $values[] = $keys[] = $item['id']; + $values[] = $item['randomNumber']; + } else { + $update->execute([$item['randomNumber'], $item['id']]); + } } - return json_encode($results, JSON_NUMERIC_CHECK); + if ($driver == 'pgsql') { + $update->execute([...$values, ...$keys]); + } + return json_encode($results); } } class Connection { + private static PDO $pdo; + private static string $driver; + private static array $updates = []; private static PDOStatement $db; private static PDOStatement $fortune; private static PDOStatement $random; - private static PDOStatement $update; private static PDOStatement $query; public static function init(string $driver): void @@ -87,15 +87,16 @@ public static function init(string $driver): void "benchmarkdbpass", [ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => false + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => false ] ); - self::$db = self::$random = self::$query = $pdo->prepare(Operation::WORLD_SELECT_SQL); + self::$db = self::$random = self::$query = $pdo->prepare(Operation::WORLD_SELECT_SQL); self::$fortune = $pdo->prepare(Operation::FORTUNE_SQL); - self::$update = $pdo->prepare(Operation::WORLD_UPDATE_SQL); + self::$pdo = $pdo; + self::$driver = $driver; } public static function db(): string @@ -115,13 +116,20 @@ public static function query(int $queries): string public static function updates(int $queries): string { - return Operation::updates(self::$random, self::$update, $queries); + if (!isset(self::$updates[$queries])) { + self::$updates[$queries] = self::$driver == 'pgsql' + ? self::$pdo->prepare('UPDATE World SET randomNumber = CASE id'.\str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $queries).'END WHERE id IN ('.\str_repeat('?::INTEGER,', $queries - 1).'?::INTEGER)') + : self::$pdo->prepare(Operation::WORLD_UPDATE_SQL); + } + + return Operation::updates(self::$random, self::$updates[$queries], $queries, self::$driver); } } class Connections { private static PDOPool $pool; + private static string $driver; public static function init(string $driver): void { @@ -133,22 +141,22 @@ public static function init(string $driver): void ->withUsername('benchmarkdbuser') ->withPassword('benchmarkdbpass'); - self::$pool = new PDOPool($config, intval(1024 / swoole_cpu_num())); + self::$pool = new PDOPool($config, intval(1400 / swoole_cpu_num())); + self::$driver = $driver; } public static function db(): string { - $pdo = self::get(); - $result = Operation::db($pdo->prepare(Operation::WORLD_SELECT_SQL)); + $pdo = self::get(); + $result = Operation::db(self::getStatement($pdo, 'select')); self::put($pdo); - return $result; } public static function fortunes(): string { - $pdo = self::get(); - $result = Operation::fortunes($pdo->prepare(Operation::FORTUNE_SQL)); + $pdo = self::get(); + $result = Operation::fortunes(self::getStatement($pdo, 'fortunes')); self::put($pdo); return $result; @@ -156,8 +164,8 @@ public static function fortunes(): string public static function query(int $queries): string { - $pdo = self::get(); - $result = Operation::query($pdo->prepare(Operation::WORLD_SELECT_SQL), $queries); + $pdo = self::get(); + $result = Operation::query(self::getStatement($pdo, 'select'), $queries); self::put($pdo); return $result; @@ -165,8 +173,8 @@ public static function query(int $queries): string public static function updates(int $queries): string { - $pdo = self::get(); - $result = Operation::updates($pdo->prepare(Operation::WORLD_SELECT_SQL), $pdo->prepare(Operation::WORLD_UPDATE_SQL), $queries); + $pdo = self::get(); + $result = Operation::updates(self::getStatement($pdo, 'select'), self::getStatement($pdo, 'update', $queries), $queries, self::$driver); self::put($pdo); return $result; @@ -181,4 +189,17 @@ private static function put(PDO|PDOProxy $db): void { self::$pool->put($db); } + + private static function getStatement(PDO|PDOProxy $pdo, string $type, int $queries = 0): PDOStatement|PDOStatementProxy + { + if ('select' == $type) { + return $pdo->prepare(Operation::WORLD_SELECT_SQL); + } elseif ('fortunes' == $type) { + return $pdo->prepare(Operation::FORTUNE_SQL); + } else { + return self::$driver == 'pgsql' + ? $pdo->prepare('UPDATE World SET randomNumber = CASE id'.\str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $queries).'END WHERE id IN ('.\str_repeat('?::INTEGER,', $queries - 1).'?::INTEGER)') + : $pdo->prepare(Operation::WORLD_UPDATE_SQL); + } + } } diff --git a/frameworks/PHP/swoole/swoole-async-mysql.dockerfile b/frameworks/PHP/swoole/swoole-async-mysql.dockerfile index 9b9daf07c43..786f80c88cd 100644 --- a/frameworks/PHP/swoole/swoole-async-mysql.dockerfile +++ b/frameworks/PHP/swoole/swoole-async-mysql.dockerfile @@ -1,7 +1,8 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.1 +ENV SWOOLE_VERSION 5.1.5 ENV ENABLE_COROUTINE 1 +ENV CPU_MULTIPLES 1 ENV DATABASE_DRIVER mysql ARG DEBIAN_FRONTEND=noninteractive @@ -10,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 php8.3-cli php8.3-pdo-mysql php8.3-dev -y > /dev/null \ + && apt install libbrotli-dev php8.3-cli php8.3-pdo-mysql php8.3-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 -j8 > /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 "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole -ADD ./php.ini /swoole ADD ./database.php /swoole -RUN cat /swoole/php.ini >> /etc/php/8.3/cli/php.ini +COPY 10-opcache.ini /etc/php/8.3/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 d75390c922e..b658f569f0e 100644 --- a/frameworks/PHP/swoole/swoole-async-postgres.dockerfile +++ b/frameworks/PHP/swoole/swoole-async-postgres.dockerfile @@ -1,7 +1,8 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.1 +ENV SWOOLE_VERSION 5.1.5 ENV ENABLE_COROUTINE 1 +ENV CPU_MULTIPLES 1 ENV DATABASE_DRIVER pgsql ARG DEBIAN_FRONTEND=noninteractive @@ -10,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 php8.3-cli php8.3-pdo-pgsql php8.3-dev libpq-dev -y > /dev/null \ + && apt install libbrotli-dev php8.3-cli php8.3-pdo-pgsql php8.3-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 -j8 > /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 "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole -ADD ./php.ini /swoole ADD ./database.php /swoole -RUN cat /swoole/php.ini >> /etc/php/8.3/cli/php.ini +COPY 10-opcache.ini /etc/php/8.3/cli/conf.d/10-opcache.ini EXPOSE 8080 CMD php /swoole/swoole-server.php diff --git a/frameworks/PHP/swoole/swoole-server.php b/frameworks/PHP/swoole/swoole-server.php index 8de85f5c0a1..38a4152476f 100644 --- a/frameworks/PHP/swoole/swoole-server.php +++ b/frameworks/PHP/swoole/swoole-server.php @@ -8,22 +8,21 @@ $enableCoroutine = getenv('ENABLE_COROUTINE') == 1; $connection = $enableCoroutine ? Connections::class : Connection::class; -$server = new Server('0.0.0.0', 8080); $setting = [ - 'worker_num' => swoole_cpu_num() * 4, + 'worker_num' => swoole_cpu_num() * ((int) getenv('CPU_MULTIPLES')), 'log_file' => '/dev/null', 'enable_coroutine' => $enableCoroutine, - 'enable_reuse_port' => true + 'enable_reuse_port' => true, + 'http_compression' => false ]; if ($enableCoroutine) { $setting['hook_flags'] = SWOOLE_HOOK_ALL; - $setting['worker_num'] = swoole_cpu_num(); } +$server = new Server('0.0.0.0', 8080); $server->set($setting); - $server->on('workerStart', function () use ($connection) { $connection::init(getenv('DATABASE_DRIVER')); }); @@ -48,16 +47,18 @@ $res->end($connection::fortunes()); break; case '/query': + $queries = isset($req->get['queries']) ? (int) $req->get['queries'] : -1; + $query_count = $queries > 1 ? min($queries, 500) : 1; + $res->header['Content-Type'] = 'application/json'; - $res->end($connection::query( - isset($req->get['queries']) ? (int) $req->get['queries'] : -1 - )); + $res->end($connection::query($query_count)); break; case '/updates': + $queries = isset($req->get['queries']) ? (int) $req->get['queries'] : -1; + $query_count = $queries > 1 ? min($queries, 500) : 1; + $res->header['Content-Type'] = 'application/json'; - $res->end($connection::updates( - isset($req->get['queries']) ? (int) $req->get['queries'] : -1 - )); + $res->end($connection::updates($query_count)); break; default: diff --git a/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile b/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile index 4893a1aad1a..86304d151fc 100644 --- a/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile +++ b/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile @@ -1,7 +1,8 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.1 +ENV SWOOLE_VERSION 5.1.5 ENV ENABLE_COROUTINE 0 +ENV CPU_MULTIPLES 1 ENV DATABASE_DRIVER mysql ARG DEBIAN_FRONTEND=noninteractive @@ -10,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 php8.3-cli php8.3-pdo-mysql php8.3-dev -y > /dev/null \ + && apt install libbrotli-dev php8.3-cli php8.3-pdo-mysql php8.3-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 -j8 > /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 "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole -ADD ./php.ini /swoole ADD ./database.php /swoole -RUN cat /swoole/php.ini >> /etc/php/8.3/cli/php.ini +COPY 10-opcache.ini /etc/php/8.3/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 9559267ce6c..8054b66912b 100644 --- a/frameworks/PHP/swoole/swoole-sync-postgres.dockerfile +++ b/frameworks/PHP/swoole/swoole-sync-postgres.dockerfile @@ -1,7 +1,8 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.1 +ENV SWOOLE_VERSION 5.1.5 ENV ENABLE_COROUTINE 0 +ENV CPU_MULTIPLES 4 ENV DATABASE_DRIVER pgsql ARG DEBIAN_FRONTEND=noninteractive @@ -10,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 php8.3-cli php8.3-pdo-pgsql php8.3-dev libpq-dev -y > /dev/null \ + && apt install libbrotli-dev php8.3-cli php8.3-pdo-pgsql php8.3-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 -j2 > /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 "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole -ADD ./php.ini /swoole ADD ./database.php /swoole -RUN cat /swoole/php.ini >> /etc/php/8.3/cli/php.ini +COPY 10-opcache.ini /etc/php/8.3/cli/conf.d/10-opcache.ini EXPOSE 8080 CMD php /swoole/swoole-server.php diff --git a/frameworks/PHP/symfony/public/swoole.php b/frameworks/PHP/symfony/public/swoole.php index 2c0695003f4..a1825fc344e 100644 --- a/frameworks/PHP/symfony/public/swoole.php +++ b/frameworks/PHP/symfony/public/swoole.php @@ -15,6 +15,6 @@ require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; -return function (array $context) { +return function (array $context): Kernel { return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; diff --git a/frameworks/PHP/symfony/symfony-swoole.dockerfile b/frameworks/PHP/symfony/symfony-swoole.dockerfile index 37403a24c97..e8fda091bb3 100644 --- a/frameworks/PHP/symfony/symfony-swoole.dockerfile +++ b/frameworks/PHP/symfony/symfony-swoole.dockerfile @@ -1,16 +1,11 @@ -FROM php:8.3-cli - -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole - -RUN pecl install apcu > /dev/null && \ - docker-php-ext-enable apcu +FROM phpswoole/swoole:5.1.3-php8.3 RUN apt-get update -yqq && \ - apt-get install -yqq libpq-dev libicu-dev git unzip > /dev/null && \ + apt-get install -yqq libpq-dev libicu-dev > /dev/null && \ docker-php-ext-install pdo_pgsql opcache intl > /dev/null -COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer +RUN pecl install apcu > /dev/null && \ + docker-php-ext-enable apcu COPY --link deploy/swoole/php.ini /usr/local/etc/php/ WORKDIR /symfony diff --git a/frameworks/PHP/ubiquity/ubiquity-swoole-mysql.dockerfile b/frameworks/PHP/ubiquity/ubiquity-swoole-mysql.dockerfile index d4195a1d945..7a99706faaf 100644 --- a/frameworks/PHP/ubiquity/ubiquity-swoole-mysql.dockerfile +++ b/frameworks/PHP/ubiquity/ubiquity-swoole-mysql.dockerfile @@ -1,29 +1,17 @@ -FROM php:8.3-cli +FROM phpswoole/swoole:5.1.3-php8.3 -RUN apt-get update > /dev/null - -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole - -RUN docker-php-ext-install pdo_mysql opcache pcntl > /dev/null +RUN docker-php-ext-install pcntl opcache > /dev/null COPY deploy/conf/php-async.ini /usr/local/etc/php/php.ini -ADD ./ /ubiquity WORKDIR /ubiquity +ADD --link . . RUN chmod -R 777 /ubiquity -RUN ["chmod", "+x", "deploy/run/install-composer.sh"] - -RUN deploy/run/install-composer.sh - -RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq git unzip > /dev/null - -RUN php composer.phar require phpmv/ubiquity-devtools:dev-master phpmv/ubiquity-swoole:dev-master --quiet +RUN composer require phpmv/ubiquity-devtools:dev-master phpmv/ubiquity-swoole:dev-master --quiet -RUN php composer.phar install --optimize-autoloader --classmap-authoritative --no-dev --quiet +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet RUN chmod 777 -R /ubiquity/.ubiquity/* @@ -36,4 +24,4 @@ COPY deploy/conf/swoole/mysql/swooleServices.php app/config/swooleServices.php EXPOSE 8080 -CMD /ubiquity/vendor/bin/Ubiquity serve -t=swoole -p=8080 -h=0.0.0.0 +ENTRYPOINT [ "/ubiquity/vendor/bin/Ubiquity", "serve", "-t=swoole", "-p=8080", "-h=0.0.0.0" ] diff --git a/frameworks/PHP/ubiquity/ubiquity-swoole.dockerfile b/frameworks/PHP/ubiquity/ubiquity-swoole.dockerfile index ff57c3c54e1..d4807c4a8ad 100644 --- a/frameworks/PHP/ubiquity/ubiquity-swoole.dockerfile +++ b/frameworks/PHP/ubiquity/ubiquity-swoole.dockerfile @@ -1,9 +1,4 @@ -FROM php:8.3-cli - -RUN apt-get update > /dev/null - -RUN pecl install swoole > /dev/null && \ - docker-php-ext-enable swoole +FROM phpswoole/swoole:5.1.3-php8.3 RUN apt-get install -y libpq-dev \ && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ @@ -11,21 +6,14 @@ RUN apt-get install -y libpq-dev \ COPY deploy/conf/php-async.ini /usr/local/etc/php/php.ini -ADD ./ /ubiquity WORKDIR /ubiquity +ADD --link . . RUN chmod -R 777 /ubiquity -RUN ["chmod", "+x", "deploy/run/install-composer.sh"] - -RUN deploy/run/install-composer.sh - -RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq git unzip > /dev/null - -RUN php composer.phar require phpmv/ubiquity-devtools:dev-master phpmv/ubiquity-swoole:dev-master --quiet +RUN composer require phpmv/ubiquity-devtools:dev-master phpmv/ubiquity-swoole:dev-master --quiet -RUN php composer.phar install --optimize-autoloader --classmap-authoritative --no-dev --quiet +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet RUN chmod 777 -R /ubiquity/.ubiquity/* @@ -38,4 +26,4 @@ COPY deploy/conf/swoole/pgsql/swooleServices.php app/config/swooleServices.php EXPOSE 8080 -CMD /ubiquity/vendor/bin/Ubiquity serve -t=swoole -p=8080 -h=0.0.0.0 +ENTRYPOINT [ "/ubiquity/vendor/bin/Ubiquity", "serve", "-t=swoole", "-p=8080", "-h=0.0.0.0" ] diff --git a/frameworks/PHP/webman/benchmark_config.json b/frameworks/PHP/webman/benchmark_config.json index 864deacb0bf..7e185ce1b24 100644 --- a/frameworks/PHP/webman/benchmark_config.json +++ b/frameworks/PHP/webman/benchmark_config.json @@ -1,9 +1,29 @@ { "framework": "webman", + "maintainers": ["walkor"], "tests": [{ "default": { + "dockerfile": "webman.dockerfile", "json_url": "/json", "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "webman", + "language": "PHP", + "flavor": "PHP7", + "orm": "Raw", + "platform": "workerman", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "webman", + "notes": "", + "versus": "workerman" + }, + "pgsql": { + "dockerfile": "webman-pgsql.dockerfile", "db_url": "/db", "query_url": "/queries/", "update_url": "/updates/", @@ -20,7 +40,7 @@ "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "webman", + "display_name": "webman-pgsql", "notes": "", "versus": "workerman" } diff --git a/frameworks/PHP/webman/config.toml b/frameworks/PHP/webman/config.toml index b4cfc7e64d6..3aa7a920672 100644 --- a/frameworks/PHP/webman/config.toml +++ b/frameworks/PHP/webman/config.toml @@ -17,3 +17,21 @@ orm = "Raw" platform = "workerman" webserver = "None" versus = "workerman" + + +[pgsql] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries/" +urls.update = "/updates/" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Micro" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "workerman" +webserver = "None" +versus = "workerman" \ No newline at end of file diff --git a/frameworks/PHP/webman/config/server.php b/frameworks/PHP/webman/config/server.php index 39f256488ef..a9e2686c8dc 100644 --- a/frameworks/PHP/webman/config/server.php +++ b/frameworks/PHP/webman/config/server.php @@ -17,9 +17,10 @@ 'transport' => 'tcp', 'context' => [], 'name' => 'webman', - 'count' => cpu_count() * 4, + 'count' => cpu_count() * ( getenv('TEST_TYPE') === 'default' ? 1 : 4 ), 'user' => '', 'group' => '', + 'reuse_port' => true, 'pid_file' => runtime_path() . '/webman.pid', 'status_file' => runtime_path() . '/webman.status', 'stdout_file' => runtime_path() . '/logs/stdout.log', diff --git a/frameworks/PHP/webman/php.ini b/frameworks/PHP/webman/php.ini index f0c616f9fb2..f4817cc9e3a 100644 --- a/frameworks/PHP/webman/php.ini +++ b/frameworks/PHP/webman/php.ini @@ -1,13 +1,11 @@ +zend_extension=opcache.so opcache.enable=1 opcache.enable_cli=1 opcache.validate_timestamps=0 opcache.save_comments=0 opcache.enable_file_override=1 opcache.huge_code_pages=1 - mysqlnd.collect_statistics = Off - memory_limit = 512M - opcache.jit_buffer_size=128M -opcache.jit=tracing +opcache.jit=tracing \ No newline at end of file diff --git a/frameworks/PHP/webman/webman-pgsql.dockerfile b/frameworks/PHP/webman/webman-pgsql.dockerfile new file mode 100644 index 00000000000..491396785ea --- /dev/null +++ b/frameworks/PHP/webman/webman-pgsql.dockerfile @@ -0,0 +1,26 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE pgsql + +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: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 + +ADD ./ /webman +WORKDIR /webman + +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY php.ini /etc/php/8.3/cli/conf.d/10-opcache.ini + +EXPOSE 8080 + +CMD php /webman/start.php start diff --git a/frameworks/PHP/webman/webman.dockerfile b/frameworks/PHP/webman/webman.dockerfile index 5b64cd4aa16..20ef9d61064 100644 --- a/frameworks/PHP/webman/webman.dockerfile +++ b/frameworks/PHP/webman/webman.dockerfile @@ -1,4 +1,6 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 + +ENV TEST_TYPE default ARG DEBIAN_FRONTEND=noninteractive @@ -11,14 +13,13 @@ RUN apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-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.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini - -COPY php.ini /etc/php/8.3/cli/php.ini +RUN pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini ADD ./ /webman WORKDIR /webman RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY php.ini /etc/php/8.3/cli/conf.d/10-opcache.ini EXPOSE 8080 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..9c1cdcf0a53 --- /dev/null +++ b/frameworks/PHP/workerman/Mysql.php @@ -0,0 +1,82 @@ +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 + { + $query_count = 1; + $q = (int)$request->get('q'); + if ($q > 1) { + $query_count = min($q, 500); + } + $arr = []; + while ($query_count--) { + $this->world->execute([mt_rand(1, 10000)]); + $arr[] = $this->world->fetch(); + } + return $arr; + } + + function update($request): array + { + $query_count = 1; + $q = (int)$request->get('q'); + if ($q > 1) { + $query_count = min($q, 500); + } + $arr = []; + while ($query_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..ff290648fa1 --- /dev/null +++ b/frameworks/PHP/workerman/MysqlSwoole.php @@ -0,0 +1,91 @@ +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 + { + $query_count = 1; + $q = (int)$request->get('q'); + if ($q > 1) { + $query_count = min($q, 500); + } + $pdo = $this->pool->get(); + $stmt = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + $arr = []; + while ($query_count--) { + $stmt->execute([mt_rand(1, 10000)]); + $arr[] = $stmt->fetch(PDO::FETCH_ASSOC); + } + $this->pool->put($pdo); + return $arr; + } + + function update($request): array + { + $query_count = 1; + $q = (int)$request->get('q'); + if ($q > 1) { + $query_count = min($q, 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 ($query_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..f86dc6364af --- /dev/null +++ b/frameworks/PHP/workerman/Pgsql.php @@ -0,0 +1,65 @@ +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 + { + $query_count = 1; + $q = (int)$request->get('q'); + if ($q > 1) { + $query_count = min($q, 500); + } + $worlds = []; + while ($query_count--) { + $this->random->execute([\mt_rand(1, 10000)]); + $world = $this->random->fetch(); + $world['randomNumber'] = \mt_rand(1, 10000); + $worlds[] = $world; + } + $rows = count($worlds); + + if (!isset($this->updates[$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)'; + + $this->updates[$rows] = $this->pdo->prepare($sql); + } + + $val = []; + $keys = []; + foreach ($worlds as $world) { + $val[] = $keys[] = $world['id']; + $val[] = $world['randomNumber']; + } + + $this->updates[$rows]->execute([...$val, ...$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..1a999aac982 --- /dev/null +++ b/frameworks/PHP/workerman/PgsqlSwoole.php @@ -0,0 +1,61 @@ +withDriver('pgsql') + ->withHost('tfb-database') + ->withPort(5432) + ->withDbName('hello_world') + ->withUsername('benchmarkdbuser') + ->withPassword('benchmarkdbpass'); + + $this->pool = new PDOPool($config, $size); + } + + + function update($request): array + { + $query_count = 1; + $q = (int)$request->get('q'); + if ($q > 1) { + $query_count = min($q, 500); + } + $worlds = []; + $pdo = $this->pool->get(); + $random = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + while ($query_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 68ce09ced79..a2077767b43 100644 --- a/frameworks/PHP/workerman/benchmark_config.json +++ b/frameworks/PHP/workerman/benchmark_config.json @@ -1,9 +1,29 @@ { "framework": "workerman", + "maintainers": ["walkor"], "tests": [{ "default": { + "dockerfile": "workerman-jit.dockerfile", "json_url": "/json", "plaintext_url": "/plaintext", + "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]", + "notes": "", + "versus": "php" + }, + "pgsql": { + "dockerfile": "workerman-pgsql-jit.dockerfile", "db_url": "/db", "query_url": "/query?q=", "update_url": "/update?q=", @@ -11,6 +31,48 @@ "port": 8080, "approach": "Realistic", "classification": "Platform", + "database": "Postgres", + "framework": "workerman", + "language": "PHP", + "flavor": "PHP8", + "orm": "Raw", + "platform": "workerman", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "workerman [jit, pgsql]", + "notes": "", + "versus": "php" + }, + "mysql": { + "dockerfile": "workerman-mysql-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]", + "notes": "", + "versus": "php" + }, + "without-jit": { + "dockerfile": "workerman.dockerfile", + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", "database": "MySQL", "framework": "workerman", "language": "PHP", @@ -24,7 +86,8 @@ "notes": "", "versus": "php" }, - "pgsql": { + "pgsql-without-jit": { + "dockerfile": "workerman-pgsql.dockerfile", "db_url": "/db", "query_url": "/query?q=", "update_url": "/update?q=", @@ -41,12 +104,37 @@ "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "workerman-postgres", + "display_name": "workerman [pgsql]", "notes": "", "versus": "php" }, - "async": { + "pgsql-swow": { + "dockerfile": "workerman-pgsql-swow-jit.dockerfile", "db_url": "/db", + "query_url": "/query?q=", + "update_url": "/update?q=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "Postgres", + "framework": "workerman", + "language": "PHP", + "flavor": "PHP8", + "orm": "Raw", + "platform": "workerman", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "workerman [jit, pgsql, swow, async]", + "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", @@ -54,22 +142,18 @@ "database": "MySQL", "framework": "workerman", "language": "PHP", - "flavor": "PHP7", + "flavor": "PHP8", "orm": "Raw", "platform": "workerman", "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "workerman-async-db", + "display_name": "workerman [jit, mysql, swow, async]", "notes": "", - "versus": "php", - "tags": [ - "broken" - ] + "versus": "php" }, - "php8-jit": { - "json_url": "/json", - "plaintext_url": "/plaintext", + "pgsql-swoole": { + "dockerfile": "workerman-pgsql-swoole-jit.dockerfile", "db_url": "/db", "query_url": "/query?q=", "update_url": "/update?q=", @@ -86,8 +170,30 @@ "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "workerman-php8-jit", - "notes": "php8 jit", + "display_name": "workerman [jit, pgsql, swoole, 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 2f3bbffd22c..791e9502688 100644 --- a/frameworks/PHP/workerman/config.toml +++ b/frameworks/PHP/workerman/config.toml @@ -33,7 +33,7 @@ platform = "workerman" webserver = "None" versus = "php" -[php8-jit] +[mysql] urls.plaintext = "/plaintext" urls.json = "/json" urls.db = "/db" @@ -50,8 +50,12 @@ platform = "workerman" webserver = "None" versus = "php" -[async] +[without-jit] +urls.plaintext = "/plaintext" +urls.json = "/json" urls.db = "/db" +urls.query = "/query?q=" +urls.update = "/update?q=" urls.fortune = "/fortunes" approach = "Realistic" classification = "Platform" @@ -62,3 +66,80 @@ orm = "Raw" platform = "workerman" webserver = "None" versus = "php" + +[pgsql-without-jit] +urls.plaintext = "/plaintext" +urls.json = "/json" +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" + +[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/php-jit.ini b/frameworks/PHP/workerman/php-jit.ini index f0c616f9fb2..f4817cc9e3a 100644 --- a/frameworks/PHP/workerman/php-jit.ini +++ b/frameworks/PHP/workerman/php-jit.ini @@ -1,13 +1,11 @@ +zend_extension=opcache.so opcache.enable=1 opcache.enable_cli=1 opcache.validate_timestamps=0 opcache.save_comments=0 opcache.enable_file_override=1 opcache.huge_code_pages=1 - mysqlnd.collect_statistics = Off - memory_limit = 512M - opcache.jit_buffer_size=128M -opcache.jit=tracing +opcache.jit=tracing \ No newline at end of file diff --git a/frameworks/PHP/workerman/php.ini b/frameworks/PHP/workerman/php.ini index e12bbd2fb0c..f6be852042e 100644 --- a/frameworks/PHP/workerman/php.ini +++ b/frameworks/PHP/workerman/php.ini @@ -7,4 +7,4 @@ opcache.huge_code_pages=1 mysqlnd.collect_statistics = Off -memory_limit = 512M +memory_limit = 512M \ No newline at end of file diff --git a/frameworks/PHP/workerman/server-async.php b/frameworks/PHP/workerman/server-async.php deleted file mode 100644 index ce468df5cd6..00000000000 --- a/frameworks/PHP/workerman/server-async.php +++ /dev/null @@ -1,80 +0,0 @@ -count = (int) shell_exec('nproc') * 2; -$http_worker->onWorkerStart = static function() { - global $mysql; - - $loop = Worker::getEventLoop(); - - $mysql = new React\MySQL\Connection($loop, [ - 'host' => 'tfb-database', - 'dbname' => 'hello_world', - 'user' => 'benchmarkdbuser', - 'passwd' => 'benchmarkdbpass' - ]); - - $mysql->on('error', function($e){ - echo $e; - }); - - $mysql->connect(function ($e) {}); -}; - -$http_worker->onMessage = static function ($connection, $request) { - - global $mysql; - - switch ($request->path()) { - case '/db': - $mysql->query('SELECT id,randomNumber FROM World WHERE id='.mt_rand(1, 10000), - static function ($command) use ($connection) { - $connection->send(new Response(200, ['Content-Type' => 'application/json', 'Date' => gmdate('D, d M Y H:i:s').' GMT'], json_encode($command->resultRows, JSON_NUMERIC_CHECK))); - } - ); - return; - - case '/fortunes': - // By default use 'Content-Type: text/html; charset=utf-8'; - $mysql->query('SELECT id,message FROM Fortune', - static function ($command) use ($connection) { - $arr = $command->resultRows; - foreach ($arr as $row) { - $fortune[$row['id']] = htmlspecialchars($row['message'], ENT_QUOTES, 'UTF-8'); - } - $fortune[0] = 'Additional fortune added at request time.'; - asort($fortune); - - $html = 'Fortunes'; - foreach ($fortune as $id => $message) { - $html .= ""; - } - - $connection->send(new Response(200, ['Date' => gmdate('D, d M Y H:i:s').' GMT'], $html.'
idmessage
$id$message
')); - - } - ); - return; - - //case '/update': - // Http::header('Content-Type: application/json'); - // return $connection->send(update()); - - //case '/info': - // Http::header('Content-Type: text/plain'); - // ob_start(); - // phpinfo(); - // return $connection->send(ob_get_clean()); - - default: - $connection->send(new Response(200, [], 'Error 404')); - - } -}; - -Worker::runAll(); diff --git a/frameworks/PHP/workerman/server.php b/frameworks/PHP/workerman/server.php index 5a38cf1e0fc..a8499e4f538 100644 --- a/frameworks/PHP/workerman/server.php +++ b/frameworks/PHP/workerman/server.php @@ -1,29 +1,83 @@ count = (int) shell_exec('nproc') * 4; -$http_worker->onWorkerStart = static function () { - 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'; - }); - init(); -}; +$test_type = getenv('TEST_TYPE') ?: 'default'; +$process = getenv('PROCESS_MULTIPLIER') ?: 1; +$pool_size = getenv('POOL_SIZE') ?: 2; +$process_count = (int) shell_exec('nproc') * $process; -$http_worker->onMessage = static function ($connection, $request) { +$db = $date = null; +$http_worker = new Worker('http://0.0.0.0:8080'); +//$http_worker->reusePort = true; +$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(); +}; +if ($test_type === 'default') { + Worker::$eventLoopClass = Select::class; +} elseif (in_array($test_type, ['pgsql-swow', 'mysql-swow'])) { + Worker::$eventLoopClass = Swow::class; +} elseif (in_array($test_type, ['pgsql-swoole', 'mysql-swoole'])) { + Worker::$eventLoopClass = Swoole::class; +} - $connection->send(router($request)); - +$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 new file mode 100644 index 00000000000..6ff432c60b8 --- /dev/null +++ b/frameworks/PHP/workerman/workerman-jit.dockerfile @@ -0,0 +1,27 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE default +ENV PROCESS_MULTIPLIER 1 + +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 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 + +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-async.dockerfile b/frameworks/PHP/workerman/workerman-mysql-jit.dockerfile similarity index 64% rename from frameworks/PHP/workerman/workerman-async.dockerfile rename to frameworks/PHP/workerman/workerman-mysql-jit.dockerfile index 3730d43e58e..33ad3879954 100644 --- a/frameworks/PHP/workerman/workerman-async.dockerfile +++ b/frameworks/PHP/workerman/workerman-mysql-jit.dockerfile @@ -1,26 +1,27 @@ FROM ubuntu:24.04 +ENV TEST_TYPE mysql +ENV PROCESS_MULTIPLIER 4 + 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 > /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 libevent-dev git > /dev/null && \ - pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini - -COPY --link php.ini /etc/php/8.3/cli/php.ini + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . -RUN composer require react/mysql "^0.6" --quiet 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-async.php start +CMD php /workerman/server.php start 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..eb88aae9c88 --- /dev/null +++ b/frameworks/PHP/workerman/workerman-mysql-swoole-jit.dockerfile @@ -0,0 +1,34 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE mysql-swoole +ENV SWOOLE_VERSION 5.1.5 +ENV PROCESS_MULTIPLIER 1 +ENV POOL_SIZE 4 + +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..2e3e571dbce --- /dev/null +++ b/frameworks/PHP/workerman/workerman-mysql-swow-jit.dockerfile @@ -0,0 +1,33 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE mysql-swow +ENV PROCESS_MULTIPLIER 1 +ENV POOL_SIZE 4 + +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-php8-jit.dockerfile b/frameworks/PHP/workerman/workerman-pgsql-jit.dockerfile similarity index 66% rename from frameworks/PHP/workerman/workerman-php8-jit.dockerfile rename to frameworks/PHP/workerman/workerman-pgsql-jit.dockerfile index 528a5c312f6..dcd2ce482e9 100644 --- a/frameworks/PHP/workerman/workerman-php8-jit.dockerfile +++ b/frameworks/PHP/workerman/workerman-pgsql-jit.dockerfile @@ -1,5 +1,8 @@ FROM ubuntu:24.04 +ENV TEST_TYPE pgsql +ENV PROCESS_MULTIPLIER 4 + ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null @@ -11,18 +14,13 @@ 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 libevent-dev git > /dev/null && \ - pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini - -COPY --link php-jit.ini /etc/php/8.3/cli/php.ini + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . -RUN sed -i "s|'/app.php|'/app-pg.php|g" server.php -RUN sed -i "s|init()|DbRaw::init()|g" server.php -RUN sed -i "s|opcache.jit=off|opcache.jit=function|g" /etc/php/8.3/cli/conf.d/10-opcache.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 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..fbc563d68f9 --- /dev/null +++ b/frameworks/PHP/workerman/workerman-pgsql-swoole-jit.dockerfile @@ -0,0 +1,34 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE pgsql-swoole +ENV SWOOLE_VERSION 5.1.5 +ENV PROCESS_MULTIPLIER 2 +ENV POOL_SIZE 16 + +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..d58d7b4865d --- /dev/null +++ b/frameworks/PHP/workerman/workerman-pgsql-swow-jit.dockerfile @@ -0,0 +1,33 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE pgsql-swow +ENV PROCESS_MULTIPLIER 2 +ENV POOL_SIZE 16 + +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 826a6a39383..fc12352dd8f 100644 --- a/frameworks/PHP/workerman/workerman-pgsql.dockerfile +++ b/frameworks/PHP/workerman/workerman-pgsql.dockerfile @@ -1,5 +1,8 @@ FROM ubuntu:24.04 +ENV TEST_TYPE pgsql +ENV PROCESS_MULTIPLIER 4 + ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null @@ -11,17 +14,13 @@ 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 libevent-dev git > /dev/null && \ - pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini - -COPY --link php.ini /etc/php/8.3/cli/php.ini + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . -RUN sed -i "s|'/app.php|'/app-pg.php|g" server.php -RUN sed -i "s|init()|DbRaw::init()|g" server.php - RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY php.ini /etc/php/8.3/cli/php.ini EXPOSE 8080 diff --git a/frameworks/PHP/workerman/workerman.dockerfile b/frameworks/PHP/workerman/workerman.dockerfile index 5d09ee6362a..6cff7987f3a 100644 --- a/frameworks/PHP/workerman/workerman.dockerfile +++ b/frameworks/PHP/workerman/workerman.dockerfile @@ -1,5 +1,8 @@ FROM ubuntu:24.04 +ENV TEST_TYPE default +ENV PROCESS_MULTIPLIER 1 + ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null @@ -11,14 +14,13 @@ 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 libevent-dev git > /dev/null && \ - pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini - -COPY --link php-jit.ini /etc/php/8.3/cli/php.ini + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/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 EXPOSE 8080 diff --git a/frameworks/Pascal/mormot/setup_and_build.sh b/frameworks/Pascal/mormot/setup_and_build.sh index 85863798754..62bd68746f8 100755 --- a/frameworks/Pascal/mormot/setup_and_build.sh +++ b/frameworks/Pascal/mormot/setup_and_build.sh @@ -27,7 +27,7 @@ rm -rf ./libs mkdir -p ./libs/mORMot/static # echo "Getting the latest pre-release URL..." # USED_TAG=$(wget -qO- https://api.github.com/repos/synopse/mORMot2/releases/latest | jq -r '.tag_name') -USED_TAG="2.2.stable" +USED_TAG="2.3.stable" echo "Used release tag $USED_TAG" URL="https://github.com/synopse/mORMot2/releases/download/$USED_TAG/mormot2static.tgz" @@ -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/f0fc66c954cd45f5c581e52c21170923805a683b -#URL="https://api.github.com/repos/synopse/mORMot2/tarball/$USED_TAG" +#URL=https://github.com/synopse/mORMot2/tarball/6dc09ceca456931384857b383ed61b63f11f3be7 +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/Perl/dancer/README.md b/frameworks/Perl/dancer/README.md index c2afcf7639c..56b1d27d773 100644 --- a/frameworks/Perl/dancer/README.md +++ b/frameworks/Perl/dancer/README.md @@ -8,7 +8,7 @@ * Dancer * Dancer::Plugin::Database -* DBD::mysql +* DBD::MariaDB * Starman (if using Starman as web server) * Plack (for plackup) * nginx (if you want to front Dancer with nginx, nginx.conf provided) @@ -22,3 +22,4 @@ Something along the lines of if you want to front it with nginx, otherwise plackup -E production -s Starman --port=8080 --workers=2 -a ./app.pl + diff --git a/frameworks/Perl/dancer/app.pl b/frameworks/Perl/dancer/app.pl index dd58de39896..65ab9d2a6a3 100755 --- a/frameworks/Perl/dancer/app.pl +++ b/frameworks/Perl/dancer/app.pl @@ -8,8 +8,8 @@ set serializer => 'JSON'; -my $dsn = "dbi:mysql:database=hello_world;host=tfb-database;port=3306"; -my $dbh = DBI->connect( $dsn, 'benchmarkdbuser', 'benchmarkdbpass', { mysql_auto_reconnect=>1 } ); +my $dsn = "dbi:MariaDB:database=hello_world;host=tfb-database;port=3306"; +my $dbh = DBI->connect( $dsn, 'benchmarkdbuser', 'benchmarkdbpass' ); my $sth = $dbh->prepare("SELECT * FROM World where id = ?"); get '/json' => sub { @@ -20,7 +20,7 @@ my $queries = params->{queries} || 1; $queries = 1 if ( $queries !~ /^\d+$/ || $queries < 1 ); $queries = 500 if $queries > 500; - + my @response; for ( 1 .. $queries ) { my $id = int rand 10000 + 1; @@ -42,3 +42,4 @@ }; Dancer->dance; + diff --git a/frameworks/Perl/dancer/benchmark_config.json b/frameworks/Perl/dancer/benchmark_config.json index 94408b910f7..306909555ae 100644 --- a/frameworks/Perl/dancer/benchmark_config.json +++ b/frameworks/Perl/dancer/benchmark_config.json @@ -19,7 +19,8 @@ "display_name": "dancer", "notes": "", "versus": "", - "tags": ["broken"] + "tags": [] } }] } + diff --git a/frameworks/Perl/dancer/dancer.dockerfile b/frameworks/Perl/dancer/dancer.dockerfile index 687c3825ae9..0094ff8fca8 100644 --- a/frameworks/Perl/dancer/dancer.dockerfile +++ b/frameworks/Perl/dancer/dancer.dockerfile @@ -1,4 +1,4 @@ -FROM perl:5.26 +FROM perl:5.40 RUN apt-get update -yqq && apt-get install -yqq nginx @@ -10,7 +10,7 @@ RUN cpanm --notest --no-man-page \ Dancer@1.3134 \ Dancer::Plugin::Database@2.10 \ DBI@1.633 \ - DBD::mysql@4.033 \ + DBD::MariaDB@1.23 \ JSON::XS@3.01 \ Plack@1.0034 \ Starman@0.4011 @@ -18,4 +18,5 @@ RUN cpanm --notest --no-man-page \ EXPOSE 8080 CMD nginx -c /dancer/nginx.conf && \ - plackup -E production -s Starman --workers=$(nproc) -l /tmp/perl-dancer.sock -a ./app.pl + plackup -E production -s Starman --workers=$(nproc) --max-requests=100000 -l /tmp/perl-dancer.sock -a ./app.pl + diff --git a/frameworks/Perl/kelp/README.md b/frameworks/Perl/kelp/README.md index f66a3470d67..8be16d73c02 100644 --- a/frameworks/Perl/kelp/README.md +++ b/frameworks/Perl/kelp/README.md @@ -1,18 +1,17 @@ # Setup -* Perl 5.10+ -* MySQL 5.5 -* MongoDB -* Wrk 2.0 +* Perl 5.36+ +* MariaDB or MongoDB # Requirements * Kelp (install from CPAN) -* Kelp::Module::JSON::XS (install from CPAN) * Kelp::Module::Template::Toolkit (install from CPAN) -* DBD::mysql (install from CPAN) +* DBI + DBD::mysql or MongoDB (install from CPAN) +* Gazelle (install from CPAN) * Starman (install from CPAN) -* MongoDB (install from CPAN) +* Starlet (install from CPAN) +* Twiggy::Prefork (install from CPAN) * nginx (if you want to front with nginx, nginx.conf provided) # Deployment @@ -24,16 +23,38 @@ ./uwsgi --plugins psgi --init app.ini -## Plack + Starman +## Plack + plack handler -1. Deploy via plackup +Recommended handler is `Gazelle`. - plackup -E deployment -s Starman --workers=25 -l /tmp/frameworks-benchmark.sock -a ./app.pl +1. Deploy via `start_server`, if you want to front it with nginx. -2. If you want to front it with nginx, otherwise + start_server --path /tmp/perl-kelp.sock --backlog 16384 -- plackup -E production -s Gazelle --max-workers=25 --max-reqs-per-child=10000 -a ./app.psgi - plackup -E deployment -s Starman --port=8080 --workers=25 -a ./app.pl +2. Otherwise + + plackup -E deployment -s Gazelle --port=8080 --max-workers=25 -a ./app.psgi + +# Code information + +`lib/KelpBench.pm` contains all action-handling and helper code. It is a full +Kelp app with `Template::Toolkit` module and standard Kelp configuration files. +While it could've been coded as a one-file Kelp app, full app style gives us +more control on the behavior of the app. It lazy-loads `DBI.pm` or `Mongo.pm` +from `lib/KelpBench/` directory based on environmental variable `MONGO`, so it +only needs one database driver at a time. + +The app is written in a relaxed style, not trying very hard to achieve the best +possible result. It very much resembles production code. For example, a proper +templating engine is used to produce the HTML document instead of inline HTML +(which is obviously much faster). + +App can be tested using mock database by running `prove -l`. In this case, it +only requires `Kelp` and `Kelp::Module::Template::Toolkit` from CPAN to be +installed. # Expert contact +@bbrtj (contact@bbrtj.eu) @naturalist (minimal@cpan.org) + diff --git a/frameworks/Perl/kelp/app.ini b/frameworks/Perl/kelp/app.ini index 7a53ad301ae..ebeb49abedf 100644 --- a/frameworks/Perl/kelp/app.ini +++ b/frameworks/Perl/kelp/app.ini @@ -1,4 +1,5 @@ [uwsgi] http-socket = :8080 -psgi = app.pl +psgi = app.psgi disable-logging = True + diff --git a/frameworks/Perl/kelp/app.pl b/frameworks/Perl/kelp/app.pl deleted file mode 100755 index 8db36540926..00000000000 --- a/frameworks/Perl/kelp/app.pl +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env perl - -use Kelp::Less; -use HTML::Escape 'escape_html'; -use MongoDB; -use DBI; -use utf8; - -module 'JSON::XS'; - -my $mongo; -my $mdb; -my $world; -my $fortune; -my @sth; -my $dbh; - -if ($ENV{MONGO}) { - $mongo = MongoDB::MongoClient->new( host => 'tfb-database', port => 27017 ); - $mdb = $mongo->get_database('hello_world'); - $world = $mdb->get_collection('world'); - $fortune = $mdb->get_collection('fortune'); -} else { - $dbh = DBI->connect( - "dbi:mysql:database=hello_world;host=tfb-database;port=3306", - 'benchmarkdbuser', - 'benchmarkdbpass', - { RaiseError => 0, PrintError => 0, mysql_enable_utf8 => 1 } - ); - @sth = map { $dbh->prepare($_) } ( - "SELECT * FROM World WHERE id = ?", - "SELECT * FROM Fortune", - "UPDATE World SET randomNumber = ? WHERE id = ?", - ); -} - -get '/json' => sub { - { message => 'Hello, World!' }; -}; - -get '/db/?db' => sub { - my ( $self, $db ) = @_; - my $id = int rand 10000 + 1; - my $row; - if ( $db eq 'mongo' ) { - $row = $world->find_one( { _id => $id } ); - } - else { - $sth[0]->execute($id); - $row = $sth[0]->fetchrow_hashref; - } - return { id => $id, randomNumber => $row->{randomNumber} }; -}; - -get '/queries/?db' => sub { - my ( $self, $db ) = @_; - query( $db // 'mongo', $self->param('queries') ); -}; - -get '/fortunes/?db' => sub { - my ( $self, $db ) = @_; - $db //= 'mongo'; - my @objects; - if ( $db eq 'mongo' ) { - my $cursor = $fortune->query( {} ); - @objects = $cursor->all; - } - else { - $sth[1]->execute(); - @objects = @{ $sth[1]->fetchall_arrayref( {} ) }; - } - push @objects, { id => 0, message => "Additional fortune added at request time." }; - fortunes( \@objects ); -}; - -get '/update/?db' => sub { - my ( $self, $db ) = @_; - $db //= 'mongo'; - - my $arr = query( $db, $self->param('queries') ); - $arr = [$arr] unless ref($arr) eq 'ARRAY'; - for my $row (@$arr) { - $row->{randomNumber} = int( rand(10_000) ) + 1; - if ( $db eq 'mongo' ) { - $world->update( { _id => $row->{id} }, - { randomNumber => $row->{randomNumber} } ); - } - else { - $row->{randomNumber} = int( rand(10_000) ) + 1; - $sth[2]->execute( $row->{randomNumber}, $row->{id} ); - } - } - - return $arr; -}; - -get '/plaintext' => sub { - shift->res->text->render('Hello, World!'); -}; - -run; - -sub query { - my ( $db, $count ) = @_; - $count //= 1; - $count = 1 if ( $count !~ /^\d+$/ || $count < 1 ); - $count = 500 if $count > 500; - my @response; - for ( 1 .. $count ) { - my $id = int rand 10000 + 1; - my $row; - if ( $db eq 'mongo' ) { - $row = $world->find_one( { _id => $id } ); - } - else { - $sth[0]->execute($id); - $row = $sth[0]->fetchrow_hashref; - } - if ($row) { - push @response, - { id => $id, randomNumber => $row->{randomNumber} }; - } - } - return \@response; -} - -sub fortunes { - my ($objects) = @_; - my $res = q[Fortunes]; - $res .= q[]; - - for my $item ( sort { $a->{message} cmp $b->{message} } @$objects ) { - my $id = $item->{id}; - my $message = escape_html( $item->{message} ); - - # HTML::Escape encodes apostrophe as ' because IE8 does not - # support '. We forse an ' here in order to pass the - # test - $message =~ s/'/&apos/g; - $res .= ""; - } - - $res .= q[
idmessage
$id$message
]; - return $res; -} diff --git a/frameworks/Perl/kelp/app.psgi b/frameworks/Perl/kelp/app.psgi new file mode 100755 index 00000000000..5a8182549bd --- /dev/null +++ b/frameworks/Perl/kelp/app.psgi @@ -0,0 +1,8 @@ +#!/usr/bin/env perl + +use Path::Tiny qw(path); +use lib path(__FILE__)->parent->child('lib'); +use KelpBench; + +KelpBench->new->run; + diff --git a/frameworks/Perl/kelp/benchmark_config.json b/frameworks/Perl/kelp/benchmark_config.json index 036f68fd31f..dc164a11576 100644 --- a/frameworks/Perl/kelp/benchmark_config.json +++ b/frameworks/Perl/kelp/benchmark_config.json @@ -2,10 +2,60 @@ "framework": "kelp", "tests": [{ "default": { - "db_url": "/db/mysql", - "query_url": "/queries/mysql?queries=", - "fortune_url": "/fortunes/mysql", + "dockerfile": "kelp.dockerfile", "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MySQL", + "framework": "kelp", + "language": "Perl", + "orm": "Raw", + "platform": "Plack", + "webserver": "Gazelle", + "os": "Linux", + "database_os": "Linux", + "notes": "", + "versus": "", + "tags": [] + }, + "gazelle-mongodb": { + "dockerfile": "kelp.dockerfile", + "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MongoDB", + "framework": "kelp", + "language": "Perl", + "orm": "Raw", + "platform": "Plack", + "webserver": "Gazelle", + "os": "Linux", + "database_os": "Linux", + "notes": "", + "versus": "", + "tags": [] + }, + + "starman-mysql": { + "dockerfile": "kelp.dockerfile", + "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", "port": 8080, "approach": "Realistic", "classification": "Fullstack", @@ -17,16 +67,18 @@ "webserver": "Starman", "os": "Linux", "database_os": "Linux", - "display_name": "kelp", "notes": "", "versus": "", - "tags": ["broken"] + "tags": [] }, - "mongodb": { - "db_url": "/db/mongo", - "query_url": "/queries/mongo?queries=", - "fortune_url": "/fortunes/mongo", + "starman-mongodb": { + "dockerfile": "kelp.dockerfile", "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", "port": 8080, "approach": "Realistic", "classification": "Fullstack", @@ -38,10 +90,102 @@ "webserver": "Starman", "os": "Linux", "database_os": "Linux", - "display_name": "kelp", "notes": "", "versus": "", - "tags": ["broken"] + "tags": [] + }, + + "starlet-mysql": { + "dockerfile": "kelp.dockerfile", + "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MySQL", + "framework": "kelp", + "language": "Perl", + "orm": "Raw", + "platform": "Plack", + "webserver": "Starlet", + "os": "Linux", + "database_os": "Linux", + "notes": "", + "versus": "", + "tags": [] + }, + "starlet-mongodb": { + "dockerfile": "kelp.dockerfile", + "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MongoDB", + "framework": "kelp", + "language": "Perl", + "orm": "Raw", + "platform": "Plack", + "webserver": "Starlet", + "os": "Linux", + "database_os": "Linux", + "notes": "", + "versus": "", + "tags": [] + }, + + "twiggy-mysql": { + "dockerfile": "kelp.dockerfile", + "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MySQL", + "framework": "kelp", + "language": "Perl", + "orm": "Raw", + "platform": "Plack", + "webserver": "Twiggy", + "os": "Linux", + "database_os": "Linux", + "notes": "", + "versus": "", + "tags": [] + }, + "twiggy-mongodb": { + "dockerfile": "kelp.dockerfile", + "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MongoDB", + "framework": "kelp", + "language": "Perl", + "orm": "Raw", + "platform": "Plack", + "webserver": "Twiggy", + "os": "Linux", + "database_os": "Linux", + "notes": "", + "versus": "", + "tags": [] } }] } + diff --git a/frameworks/Perl/kelp/conf/config.pl b/frameworks/Perl/kelp/conf/config.pl new file mode 100644 index 00000000000..ce7f3527f59 --- /dev/null +++ b/frameworks/Perl/kelp/conf/config.pl @@ -0,0 +1,12 @@ +{ + modules => [qw(JSON Template::Toolkit)], + modules_init => { + 'Template::Toolkit' => { + STRICT => 1, + OUTLINE_TAG => qr{\V*%%}, # https://github.com/abw/Template2/issues/320 + ENCODING => 'utf8', + INCLUDE_PATH => 'views', + }, + }, +} + diff --git a/frameworks/Perl/kelp/conf/test.pl b/frameworks/Perl/kelp/conf/test.pl new file mode 100644 index 00000000000..f480597ed9b --- /dev/null +++ b/frameworks/Perl/kelp/conf/test.pl @@ -0,0 +1,23 @@ +{ + '+modules' => [qw(Logger)], + + modules_init => { + Logger => { + outputs => [ + [ + 'Screen', + name => 'logs', + min_level => 'debug', + stderr => 1, + newline => 1, + utf8 => 1, + ], + ], + }, + + 'Template::Toolkit' => { + DEBUG => 1, + }, + }, +} + diff --git a/frameworks/Perl/kelp/config.toml b/frameworks/Perl/kelp/config.toml deleted file mode 100644 index 7e997486fb2..00000000000 --- a/frameworks/Perl/kelp/config.toml +++ /dev/null @@ -1,32 +0,0 @@ -[framework] -name = "kelp" - -[main] -urls.plaintext = "/plaintext" -urls.db = "/db/mysql" -urls.query = "/queries/mysql?queries=" -urls.fortune = "/fortunes/mysql" -approach = "Realistic" -classification = "Fullstack" -database = "MySQL" -database_os = "Linux" -os = "Linux" -orm = "Raw" -platform = "Plack" -webserver = "Starman" -versus = "" - -[mongodb] -urls.plaintext = "/plaintext" -urls.db = "/db/mongo" -urls.query = "/queries/mongo?queries=" -urls.fortune = "/fortunes/mongo" -approach = "Realistic" -classification = "Fullstack" -database = "MongoDB" -database_os = "Linux" -os = "Linux" -orm = "Raw" -platform = "Plack" -webserver = "Starman" -versus = "" diff --git a/frameworks/Perl/kelp/kelp-mongodb.dockerfile b/frameworks/Perl/kelp/kelp-mongodb.dockerfile deleted file mode 100644 index 783c7e4d263..00000000000 --- a/frameworks/Perl/kelp/kelp-mongodb.dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM perl:5.26 - -RUN apt-get update -yqq && apt-get install -yqq nginx - -WORKDIR /kelp - -RUN cpanm --notest --no-man-page \ - JSON JSON::XS IO::Socket::IP IO::Socket::SSL \ - Kelp@0.9071 \ - DBI@1.636 \ - DBD::mysql@4.033 \ - MongoDB@1.8.1 \ - Kelp::Module::JSON::XS@0.502 \ - HTML::Escape@1.10 \ - HTTP::Parser::XS@0.17 \ - Starman@0.4014 - -ADD ./app.ini /kelp/ -ADD ./app.pl /kelp/ -ADD ./nginx.conf /kelp/ - -ENV MONGO=1 - -EXPOSE 8080 - -CMD nginx -c /kelp/nginx.conf && \ - plackup -E production -s Starman --workers=$(nproc) -l /tmp/perl-kelp.sock -a ./app.pl diff --git a/frameworks/Perl/kelp/kelp.dockerfile b/frameworks/Perl/kelp/kelp.dockerfile index 7bfd6f33521..b8bd3453317 100644 --- a/frameworks/Perl/kelp/kelp.dockerfile +++ b/frameworks/Perl/kelp/kelp.dockerfile @@ -1,25 +1,33 @@ -FROM perl:5.26 +FROM perl:5.40 + +ARG TFB_TEST_NAME +ARG TFB_TEST_DATABASE RUN apt-get update -yqq && apt-get install -yqq nginx WORKDIR /kelp RUN cpanm --notest --no-man-page \ - JSON JSON::XS IO::Socket::IP IO::Socket::SSL \ - Kelp@0.9071 \ - DBI@1.636 \ - DBD::mysql@4.033 \ - MongoDB@1.8.1 \ - Kelp::Module::JSON::XS@0.502 \ - HTML::Escape@1.10 \ - HTTP::Parser::XS@0.17 \ - Starman@0.4014 - -ADD ./app.ini /kelp/ -ADD ./app.pl /kelp/ -ADD ./nginx.conf /kelp/ + Kelp::Module::Template::Toolkit@0.301 \ + Kelp \ + DBI@1.643 \ + DBD::MariaDB@1.23 \ + MongoDB@2.2.2 \ + Cpanel::JSON::XS@4.38 \ + Gazelle@0.49 \ + Starman@0.4017 \ + Starlet@0.31 \ + Twiggy::Prefork@0.08 \ + Net::Server::SS::PreFork@0.05 + +ADD ./ /kelp/ + +ENV TEST_NAME=$TFB_TEST_NAME +ENV DATABASE=$TFB_TEST_DATABASE +ENV MAX_REQS=100000 +ENV SOCKET_FILE=/tmp/perl-kelp.sock EXPOSE 8080 -CMD nginx -c /kelp/nginx.conf && \ - plackup -E production -s Starman --workers=$(nproc) -l /tmp/perl-kelp.sock -a ./app.pl +CMD nginx -c /kelp/nginx.conf && ./run.pl + diff --git a/frameworks/Perl/kelp/lib/KelpBench.pm b/frameworks/Perl/kelp/lib/KelpBench.pm new file mode 100644 index 00000000000..d20a72a31ed --- /dev/null +++ b/frameworks/Perl/kelp/lib/KelpBench.pm @@ -0,0 +1,130 @@ +package KelpBench; + +use v5.36; +use Kelp::Base 'Kelp'; + +## Attributes + +attr database => sub { + if (lc $ENV{DATABASE} eq 'mongodb') { + require KelpBench::Mongo; + return KelpBench::Mongo->new; + } + elsif (lc $ENV{DATABASE} eq 'mysql') { + require KelpBench::DBI; + return KelpBench::DBI->new; + } + else { + die "unknown database chosen: $ENV{DATABASE}"; + } +}; + +## Utilities + +sub validate_number ($self, $num, $min, $max) +{ + return $min unless length($num // '') && $num !~ /\D/; + return $min if $num < $min; + return $max if $num > $max; + return $num; +} + +sub random_number ($self, $max = 10_000) +{ + return int(rand($max) + 1); +} + +sub random_id ($self) +{ + # in case random ids were not the same as random numbers + return $self->random_number(10_000); +} + +sub get_random_entries ($self, $count) +{ + $count = $self->validate_number($count, 1, 500); + + my @result; + for (1 .. $count) { + my $id = $self->random_id; + my $row = $self->database->random_number($id); + next unless $row; + + push @result, { + id => $id, + randomNumber => $row->{randomNumber} + }; + } + + return \@result; +} + +## Framework code + +sub before_dispatch {} # skip trying to log access +sub before_finalize {} # skip adding X-Framework + +sub build ($self) +{ + $self->add_route([GET => '/plaintext'] => 'action_plaintext'); + $self->add_route([GET => '/json'] => 'action_json'); + $self->add_route([GET => '/db'] => 'action_db'); + $self->add_route([GET => '/queries'] => 'action_queries'); + $self->add_route([GET => '/fortunes'] => 'action_fortunes'); + $self->add_route([GET => '/updates'] => 'action_updates'); +} + +## Registered route handlers +## Names prefixed with _action, because we did not separate a controller +## (Controllers would slow this down a bit due to reblessing of app object) + +sub action_plaintext ($self) +{ + $self->res->text; + return 'Hello, World!'; +} + +sub action_json ($self) +{ + return { message => 'Hello, World!' }; +} + +sub action_db ($self) +{ + my $id = $self->random_id; + my $row = $self->database->random_number($id); + + return { id => $id, randomNumber => $row->{randomNumber} }; +} + +sub action_queries ($self) +{ + return $self->get_random_entries($self->req->query_param('queries')); +} + +sub action_fortunes ($self) { + my $objects = $self->database->fortune; + + push $objects->@*, { + id => 0, + message => "Additional fortune added at request time." + }; + + $objects->@* = sort { $a->{message} cmp $b->{message} } $objects->@*; + return $self->template('fortunes', { rows => $objects }); +} + +sub action_updates ($self) +{ + my $arr = $self->get_random_entries($self->req->query_param('queries')); + + foreach my $row ($arr->@*) { + $row->{randomNumber} = $self->random_number; + $self->database->update($row->@{qw(id randomNumber)}); + } + + return $arr; +}; + +1; + diff --git a/frameworks/Perl/kelp/lib/KelpBench/DBI.pm b/frameworks/Perl/kelp/lib/KelpBench/DBI.pm new file mode 100644 index 00000000000..15440bff07e --- /dev/null +++ b/frameworks/Perl/kelp/lib/KelpBench/DBI.pm @@ -0,0 +1,47 @@ +package KelpBench::DBI; + +use v5.36; +use Kelp::Base 'Kelp'; +use DBI; + +attr dbh => sub { + DBI->connect( + "dbi:MariaDB:database=hello_world;host=tfb-database;port=3306", + 'benchmarkdbuser', + 'benchmarkdbpass', + { RaiseError => 1, PrintError => 0 } + ); +}; + +attr _world => sub ($self) { + $self->dbh->prepare("SELECT * FROM World WHERE id = ?"); +}; + +attr _fortune => sub ($self) { + $self->dbh->prepare("SELECT * FROM Fortune"); +}; + +attr _update => sub ($self) { + $self->dbh->prepare("UPDATE World SET randomNumber = ? WHERE id = ?"); +}; + +sub random_number ($self, $id) +{ + $self->_world->execute($id); + return $self->_world->fetchrow_hashref; +} + +sub fortune ($self) +{ + $self->_fortune->execute(); + return $self->_fortune->fetchall_arrayref({}); +} + +sub update ($self, $id, $random_number) +{ + $self->_update->execute($random_number, $id); + return; +} + +1; + diff --git a/frameworks/Perl/kelp/lib/KelpBench/Mongo.pm b/frameworks/Perl/kelp/lib/KelpBench/Mongo.pm new file mode 100644 index 00000000000..3af50e54c12 --- /dev/null +++ b/frameworks/Perl/kelp/lib/KelpBench/Mongo.pm @@ -0,0 +1,43 @@ +package KelpBench::Mongo; + +use v5.36; +use Kelp::Base 'Kelp'; +use MongoDB; + +attr dbh => sub { + MongoDB::MongoClient->new( + host => 'tfb-database', + port => 27017 + )->get_database('hello_world'); +}; + +attr _world => sub ($self) { + $self->dbh->get_collection('world'); +}; + +attr _fortune => sub ($self) { + $self->dbh->get_collection('fortune'); +}; + +sub random_number ($self, $id) +{ + return $self->_world->find_one({ _id => $id }); +} + +sub fortune ($self) +{ + return [$self->_fortune->find->all]; +} + +sub update ($self, $id, $random_number) +{ + $self->_world->update_one( + { _id => $id }, + { '$set' => { randomNumber => $random_number } }, + ); + + return; +} + +1; + diff --git a/frameworks/Perl/kelp/run.pl b/frameworks/Perl/kelp/run.pl new file mode 100755 index 00000000000..3aaed8d4cf7 --- /dev/null +++ b/frameworks/Perl/kelp/run.pl @@ -0,0 +1,82 @@ +#!/usr/bin/env perl + +use v5.36; +use Data::Dumper; + +my $max_reqs = $ENV{MAX_REQS}; +my $test_name = $ENV{TEST_NAME}; +my $socket_file = $ENV{SOCKET_FILE}; +my $app_runner = 'app.psgi'; + +my $max_workers = `nproc`; +chomp $max_workers; + +my %runner_map = ( + gazelle => [ + 'start_server', + '--path' => $socket_file, + '--backlog' => 16384, + '--', + 'plackup', + '-E' => 'production', + '-s' => 'Gazelle', + '--max-workers' => $max_workers, + '--max-reqs-per-child' => $max_reqs, + '-a' => $app_runner, + ], + starman => [ + 'start_server', + '--backlog' => 16384, + '--', + 'plackup', + '-E' => 'production', + '-s' => 'Starman', + '-l' => $socket_file, + '--workers' => $max_workers, + '--max-requests' => $max_reqs, + '-a' => $app_runner, + ], + starlet => [ + 'start_server', + '--path' => $socket_file, + '--backlog' => 16384, + '--', + 'plackup', + '-E' => 'production', + '-s' => 'Starlet', + '--max-workers' => $max_workers, + '--max-reqs-per-child' => $max_reqs, + '-a' => $app_runner, + ], + # NOTE: twiggy does not play well with Server::Starter + # NOTE: twiggy couldn't pass update tests, so I disabled them + twiggy => [ + 'plackup', + '-E' => 'production', + '-s' => 'Twiggy::Prefork', + '-l' => $socket_file, + '--backlog' => 16384, + '--max-workers' => $max_workers, + '--max-reqs-per-child' => $max_reqs, + '-a' => $app_runner, + ], +); + +# default is gazelle-mysql (techempower will warn if there is no default) +$test_name = 'kelp-gazelle-mysql' + if $test_name eq 'kelp'; + +die "invalid test name $test_name" + unless $test_name =~ m{^kelp-(\w+)-(\w+)$}; + +die 'database mismatch' + unless $2 eq $ENV{DATABASE}; + +my $command = $runner_map{$1}; +die "invalid server $1" + unless $command; + +say 'Running command: ' . Dumper($command); + +exec @$command; + diff --git a/frameworks/Perl/kelp/t/main.t b/frameworks/Perl/kelp/t/main.t index 8d816c95324..a682ca112cf 100644 --- a/frameworks/Perl/kelp/t/main.t +++ b/frameworks/Perl/kelp/t/main.t @@ -1,56 +1,107 @@ -use strict; -use warnings; +use v5.36; use utf8; use Kelp::Test; use Test::More; use Test::Deep; use HTTP::Request::Common; +use KelpBench; -my $t = Kelp::Test->new( psgi => 'app.pl'); -my $world = { randomNumber => re(qr{^\d+$}), id => re(qr{^\d+$}) }; +# use mock to avoid the need for DB modules and actual running DB +# (however, we do not test for DB code correctness this way) +package DBMock { + use v5.36; + use utf8; -subtest 'json' => sub { - $t->request( GET '/json' )->json_cmp( { message => 'Hello, World!' } ); + use Kelp::Base; + + sub random_number ($self, $id) + { + return { + id => $id, + randomNumber => int(rand(10_000) + 1), + }; + } + + sub fortune ($self) + { + return [ + { + id => 1, + message => 'フレームワークのベンチマーク', + }, + { + id => 2, + message => '', + }, + { + id => 3, + message => '&&/\\+?', + }, + ]; + } + + sub update ($self, $id, $random_number) + { + return; + } }; +my $app = KelpBench->new(mode => 'test', database => DBMock->new); +my $t = Kelp::Test->new(app => $app); +my $world = { randomNumber => re(qr{^\d+$}), id => re(qr{^\d+$}) }; + subtest plaintext => sub { - $t->request( GET '/plaintext' ) - ->content_type_is('text/plain') - ->content_is('Hello, World!'); + my $uri = '/plaintext'; + + $t->request(GET $uri) + ->content_type_is('text/plain') + ->content_is('Hello, World!'); +}; + +subtest 'json' => sub { + my $uri = '/json'; + + $t->request(GET $uri) + ->json_cmp({ message => 'Hello, World!' }); }; subtest db => sub { - for my $uri (qw{/db /db/mongo}) { - $t->request( GET $uri )->json_cmp($world); - } + my $uri = '/db'; + + $t->request(GET $uri) + ->json_cmp($world); }; subtest queries => sub { - for my $uri (qw{/queries /queries/mongo}) { - $t->request( GET $uri )->json_cmp($world); - $t->request( GET "$uri?queries=3" ) - ->json_cmp( [ $world, $world, $world ] ); - $t->request( GET "$uri?queries=0" )->json_cmp($world); - } + my $uri = '/queries'; + + $t->request(GET $uri) + ->json_cmp([$world]); + $t->request(GET "$uri?queries=3") + ->json_cmp([$world, $world, $world]); + $t->request(GET "$uri?queries=0") + ->json_cmp([$world]); }; subtest update => sub { - for my $uri (qw{/update /update/mongo}) { - $t->request( GET $uri )->json_cmp([$world]); - $t->request( GET "$uri?queries=3" ) - ->json_cmp( [ $world, $world, $world ] ); - } + my $uri = '/updates'; + + $t->request(GET $uri) + ->json_cmp([$world]); + $t->request(GET "$uri?queries=3") + ->json_cmp([ $world, $world, $world ]); }; subtest fortunes => sub { - for my $uri (qw{/fortunes /fortunes/mongo}) { - $t->request( GET $uri ) - ->content_type_is('text/html') - ->content_like(qr{<script>}) - ->content_like(qr{フレームワークのベンチマーク}) - ->content_like(qr{Additional fortune added at request time.}); - } + my $uri = '/fortunes'; + + $t->request(GET $uri) + ->content_type_is('text/html') + ->content_like(qr{<script>}) + ->content_like(qr{フレームワークのベンチマーク}) + ->content_like(qr{Additional fortune added at request time.}); }; done_testing; + diff --git a/frameworks/Perl/kelp/views/fortunes.tt b/frameworks/Perl/kelp/views/fortunes.tt new file mode 100644 index 00000000000..9667485892f --- /dev/null +++ b/frameworks/Perl/kelp/views/fortunes.tt @@ -0,0 +1,22 @@ + + + + Fortunes + + + + + + + + + %% FOREACH row IN rows + + + + + %% END +
idmessage
[% row.id %][% row.message | html %]
+ + + diff --git a/frameworks/Perl/mojolicious/app.pl b/frameworks/Perl/mojolicious/app.pl index a1e38dd48f6..465c2c7381c 100644 --- a/frameworks/Perl/mojolicious/app.pl +++ b/frameworks/Perl/mojolicious/app.pl @@ -1,18 +1,19 @@ +use v5.36; use Mojolicious::Lite; use Mojo::Pg; use Mojo::Promise; -use Cpanel::JSON::XS 'encode_json'; use Scalar::Util 'looks_like_number'; -use Data::Dumper; # configuration +use constant MAX_DB_CONCURRENCY => 50; + { my $nproc = `nproc`; app->config(hypnotoad => { - accepts => 0, - clients => int( 256 / $nproc ) + 1, + accepts => 100000, + clients => MAX_DB_CONCURRENCY, graceful_timeout => 1, requests => 10000, workers => $nproc, @@ -20,41 +21,33 @@ }); } -{ - my $db_host = 'tfb-database'; - helper pg => sub { state $pg = Mojo::Pg->new('postgresql://benchmarkdbuser:benchmarkdbpass@' . $db_host . '/hello_world')->max_connections(50) }; -} - -helper render_json => sub { - my $c = shift; - $c->res->headers->content_type('application/json'); - $c->render( data => encode_json(shift) ); -}; - # Routes -get '/json' => sub { shift->helpers->render_json({message => 'Hello, World!'}) }; +get '/json' => sub ($c) { + $c->render(json => {message => 'Hello, World!'}); +}; -get '/db' => sub { shift->helpers->render_query(1, {single => 1}) }; +get '/db' => sub ($c) { + $c->helpers->render_query(1, {single => 1}); +}; -get '/queries' => sub { - my $c = shift; +get '/queries' => sub ($c) { $c->helpers->render_query(scalar $c->param('queries')); }; -get '/fortunes' => sub { - my $c = shift; +get '/fortunes' => sub ($c) { $c->render_later; - my $docs = $c->helpers->pg->db->query_p('SELECT id, message FROM Fortune') - ->then(sub{ - my $docs = $_[0]->arrays; - push @$docs, [0, 'Additional fortune added at request time.']; - $c->render(fortunes => docs => $docs->sort(sub{ $a->[1] cmp $b->[1] }) ) - }); + + $c->helpers->pg->db->query_p('SELECT id, message FROM Fortune') + ->then(sub ($query) { + my $docs = $query->arrays; + push @$docs, [0, 'Additional fortune added at request time.']; + + $c->render(fortunes => docs => $docs->sort(sub { $a->[1] cmp $b->[1] })); + }); }; -get '/updates' => sub { - my $c = shift; +get '/updates' => sub ($c) { $c->helpers->render_query(scalar $c->param('queries'), {update => 1}); }; @@ -62,55 +55,47 @@ # Additional helpers (shared code) -helper 'render_query' => sub { - my ($self, $q, $args) = @_; +helper pg => sub { + state $pg = Mojo::Pg + ->new('postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world') + ->max_connections(MAX_DB_CONCURRENCY + 1); +}; + +helper 'render_query' => sub ($self, $q, $args = {}) { $self->render_later; - $args ||= {}; - my $update = $args->{update}; $q = 1 unless looks_like_number($q); $q = 1 if $q < 1; $q = 500 if $q > 500; - my $r = []; - my $tx = $self->tx; + Mojo::Promise->map({concurrency => MAX_DB_CONCURRENCY}, sub { + my $db = $self->helpers->pg->db; + my $id = 1 + int rand 10_000; + my $query = $db->query('SELECT id, randomnumber FROM World WHERE id=?', $id); + my $number = $query->array->[1]; - my @queries; - foreach (1 .. $q) { - my $id = 1 + int rand 10_000; + if ($args->{update}) { + $number = 1 + int rand 10_000; + $db->query('UPDATE World SET randomnumber=? WHERE id=?', $number, $id); + } - push @queries, $self->helpers->pg->db->query_p('SELECT id,randomnumber FROM World WHERE id=?', $id) - ->then(sub{ - my $randomNumber = $_[0]->array->[0]; - - return Mojo::Promise->new->resolve($id, $randomNumber) - ->then(sub{ - if($update) { - $randomNumber = 1 + int rand 10_000; - return Mojo::Promise->all( - Mojo::Promise->new->resolve($_[0], $randomNumber), - $self->helpers->pg->db->query_p('UPDATE World SET randomnumber=? WHERE id=?', $randomNumber, $id) - ) - ->then(sub { - return $_[0]; - }) - } - return [shift, shift]; - }) - }); - } + return Mojo::Promise->resolve([$id, $number]); + }, 1 .. $q) + ->then(sub (@responses) { + my @results; - Mojo::Promise->all(@queries) - ->then(sub{ - my @responses = @_; foreach my $resp (@responses) { - push @$r, { id => $resp->[0][0], randomNumber => $resp->[0][1] }; + push @results, { id => $resp->[0][0], randomNumber => $resp->[0][1] }; } - $r = $r->[0] if $args->{single}; - $self->helpers->render_json($r); - }) + if ($args->{single}) { + $self->render(json => $results[0]); + } + else { + $self->render(json => \@results); + } + }); }; app->start; @@ -133,3 +118,4 @@ + diff --git a/frameworks/Perl/mojolicious/cpanfile b/frameworks/Perl/mojolicious/cpanfile index 12f340c183e..68299a759ce 100644 --- a/frameworks/Perl/mojolicious/cpanfile +++ b/frameworks/Perl/mojolicious/cpanfile @@ -1,7 +1,8 @@ requires 'Mojolicious', '7.84'; requires 'Mojo::Pg', '4.08'; -requires 'Cpanel::JSON::XS', '4.02'; +requires 'Cpanel::JSON::XS', '4.38'; requires 'EV', '4.22'; recommends 'IO::Socket::IP', '0.36'; recommends 'IO::Socket::SSL'; + diff --git a/frameworks/Perl/mojolicious/cpanfile.snapshot b/frameworks/Perl/mojolicious/cpanfile.snapshot index a43dcb6688e..c3f7c9e074b 100644 --- a/frameworks/Perl/mojolicious/cpanfile.snapshot +++ b/frameworks/Perl/mojolicious/cpanfile.snapshot @@ -1,15 +1,15 @@ # carton snapshot format: version 1.0 DISTRIBUTIONS - Canary-Stability-2012 - pathname: M/ML/MLEHMANN/Canary-Stability-2012.tar.gz + Canary-Stability-2013 + pathname: M/ML/MLEHMANN/Canary-Stability-2013.tar.gz provides: - Canary::Stability 2012 + Canary::Stability 2013 requirements: ExtUtils::MakeMaker 0 - Class-Method-Modifiers-2.12 - pathname: E/ET/ETHER/Class-Method-Modifiers-2.12.tar.gz + Class-Method-Modifiers-2.15 + pathname: E/ET/ETHER/Class-Method-Modifiers-2.15.tar.gz provides: - Class::Method::Modifiers 2.12 + Class::Method::Modifiers 2.15 requirements: B 0 Carp 0 @@ -27,27 +27,36 @@ DISTRIBUTIONS ExtUtils::MakeMaker 0 Storable 0 perl 5.008001 - Cpanel-JSON-XS-4.02 - pathname: R/RU/RURBAN/Cpanel-JSON-XS-4.02.tar.gz + Cpanel-JSON-XS-4.38 + pathname: R/RU/RURBAN/Cpanel-JSON-XS-4.38.tar.gz provides: - Cpanel::JSON::XS 4.02 + Cpanel::JSON::XS 4.38 Cpanel::JSON::XS::Type undef requirements: + Carp 0 + Config 0 + Encode 1.9801 + Exporter 0 ExtUtils::MakeMaker 0 Pod::Text 2.08 - DBD-Pg-3.7.4 - pathname: T/TU/TURNSTEP/DBD-Pg-3.7.4.tar.gz + XSLoader 0 + overload 0 + strict 0 + warnings 0 + DBD-Pg-3.18.0 + pathname: T/TU/TURNSTEP/DBD-Pg-3.18.0.tar.gz provides: - Bundle::DBD::Pg v3.7.4 - DBD::Pg v3.7.4 + Bundle::DBD::Pg v3.18.0 + DBD::Pg v3.18.0 requirements: DBI 1.614 - ExtUtils::MakeMaker 6.11 + ExtUtils::MakeMaker 6.58 + File::Temp 0 Test::More 0.88 Time::HiRes 0 version 0 - DBI-1.641 - pathname: T/TI/TIMB/DBI-1.641.tar.gz + DBI-1.643 + pathname: T/TI/TIMB/DBI-1.643.tar.gz provides: Bundle::DBI 12.008696 DBD::DBM 0.08 @@ -103,7 +112,7 @@ DISTRIBUTIONS DBD::Sponge::dr 12.010003 DBD::Sponge::st 12.010003 DBDI 12.015129 - DBI 1.641 + DBI 1.643 DBI::Const::GetInfo::ANSI 2.008697 DBI::Const::GetInfo::ODBC 2.011374 DBI::Const::GetInfoReturn 2.008697 @@ -143,128 +152,60 @@ DISTRIBUTIONS DBI::SQL::Nano::Table_ 1.015544 DBI::Util::CacheMemory 0.010315 DBI::Util::_accessor 0.009479 - DBI::common 1.641 + DBI::common 1.643 requirements: ExtUtils::MakeMaker 6.48 Test::Simple 0.90 - perl 5.008 - Devel-GlobalDestruction-0.14 - pathname: H/HA/HAARG/Devel-GlobalDestruction-0.14.tar.gz - provides: - Devel::GlobalDestruction 0.14 - requirements: - ExtUtils::MakeMaker 0 - Sub::Exporter::Progressive 0.001011 - perl 5.006 - EV-4.22 - pathname: M/ML/MLEHMANN/EV-4.22.tar.gz + perl 5.008001 + EV-4.34 + pathname: M/ML/MLEHMANN/EV-4.34.tar.gz provides: - EV 4.22 + EV 4.34 EV::MakeMaker undef requirements: Canary::Stability 0 ExtUtils::MakeMaker 6.52 common::sense 0 - Hash-Merge-0.300 - pathname: R/RE/REHSACK/Hash-Merge-0.300.tar.gz + Hash-Merge-0.302 + pathname: H/HE/HERMES/Hash-Merge-0.302.tar.gz provides: - Hash::Merge 0.300 + Hash::Merge 0.302 requirements: Clone::Choose 0.008 ExtUtils::MakeMaker 6.64 Scalar::Util 0 perl 5.008001 - MRO-Compat-0.13 - pathname: H/HA/HAARG/MRO-Compat-0.13.tar.gz + MRO-Compat-0.15 + pathname: H/HA/HAARG/MRO-Compat-0.15.tar.gz provides: - MRO::Compat 0.13 + MRO::Compat 0.15 requirements: ExtUtils::MakeMaker 0 perl 5.006 - Module-Build-0.4224 - pathname: L/LE/LEONT/Module-Build-0.4224.tar.gz - provides: - Module::Build 0.4224 - Module::Build::Base 0.4224 - Module::Build::Compat 0.4224 - Module::Build::Config 0.4224 - Module::Build::Cookbook 0.4224 - Module::Build::Dumper 0.4224 - Module::Build::Notes 0.4224 - Module::Build::PPMMaker 0.4224 - Module::Build::Platform::Default 0.4224 - Module::Build::Platform::MacOS 0.4224 - Module::Build::Platform::Unix 0.4224 - Module::Build::Platform::VMS 0.4224 - Module::Build::Platform::VOS 0.4224 - Module::Build::Platform::Windows 0.4224 - Module::Build::Platform::aix 0.4224 - Module::Build::Platform::cygwin 0.4224 - Module::Build::Platform::darwin 0.4224 - Module::Build::Platform::os2 0.4224 - Module::Build::PodParser 0.4224 - requirements: - CPAN::Meta 2.142060 - CPAN::Meta::YAML 0.003 - Cwd 0 - Data::Dumper 0 - ExtUtils::CBuilder 0.27 - ExtUtils::Install 0 - ExtUtils::Manifest 0 - ExtUtils::Mkbootstrap 0 - ExtUtils::ParseXS 2.21 - File::Basename 0 - File::Compare 0 - File::Copy 0 - File::Find 0 - File::Path 0 - File::Spec 0.82 - File::Temp 0.15 - Getopt::Long 0 - Module::Metadata 1.000002 - Parse::CPAN::Meta 1.4401 - Perl::OSType 1 - Pod::Man 2.17 - TAP::Harness 3.29 - Test::More 0.49 - Text::Abbrev 0 - Text::ParseWords 0 - perl 5.006001 - version 0.87 - Module-Runtime-0.016 - pathname: Z/ZE/ZEFRAM/Module-Runtime-0.016.tar.gz - provides: - Module::Runtime 0.016 - requirements: - Module::Build 0 - Test::More 0.41 - perl 5.006 - strict 0 - warnings 0 - Mojo-Pg-4.08 - pathname: S/SR/SRI/Mojo-Pg-4.08.tar.gz + Mojo-Pg-4.27 + pathname: S/SR/SRI/Mojo-Pg-4.27.tar.gz provides: - Mojo::Pg 4.08 + Mojo::Pg 4.27 Mojo::Pg::Database undef Mojo::Pg::Migrations undef Mojo::Pg::PubSub undef Mojo::Pg::Results undef Mojo::Pg::Transaction undef - SQL::Abstract::Pg undef requirements: - DBD::Pg 3.005001 + DBD::Pg 3.007004 ExtUtils::MakeMaker 0 - Mojolicious 7.53 - SQL::Abstract 1.85 - perl 5.010001 - Mojolicious-7.84 - pathname: S/SR/SRI/Mojolicious-7.84.tar.gz + Mojolicious 8.50 + SQL::Abstract::Pg 1.0 + perl 5.016 + Mojolicious-9.37 + pathname: S/SR/SRI/Mojolicious-9.37.tar.gz provides: Mojo undef Mojo::Asset undef Mojo::Asset::File undef Mojo::Asset::Memory undef Mojo::Base undef + Mojo::BaseUtil undef Mojo::ByteStream undef Mojo::Cache undef Mojo::Collection undef @@ -278,6 +219,7 @@ DISTRIBUTIONS Mojo::DOM::CSS undef Mojo::DOM::HTML undef Mojo::Date undef + Mojo::DynamicMethods undef Mojo::EventEmitter undef Mojo::Exception undef Mojo::File undef @@ -286,13 +228,8 @@ DISTRIBUTIONS Mojo::Home undef Mojo::IOLoop undef Mojo::IOLoop::Client undef - Mojo::IOLoop::Delay undef Mojo::IOLoop::Server undef Mojo::IOLoop::Stream undef - Mojo::IOLoop::Stream::HTTPClient undef - Mojo::IOLoop::Stream::HTTPServer undef - Mojo::IOLoop::Stream::WebSocketClient undef - Mojo::IOLoop::Stream::WebSocketServer undef Mojo::IOLoop::Subprocess undef Mojo::IOLoop::TLS undef Mojo::JSON undef @@ -316,7 +253,6 @@ DISTRIBUTIONS Mojo::Server::Morbo::Backend undef Mojo::Server::Morbo::Backend::Poll undef Mojo::Server::PSGI undef - Mojo::Server::PSGI::_IO undef Mojo::Server::Prefork undef Mojo::Template undef Mojo::Transaction undef @@ -331,37 +267,36 @@ DISTRIBUTIONS Mojo::UserAgent::Transactor undef Mojo::Util undef Mojo::WebSocket undef - Mojolicious 7.84 + Mojolicious 9.37 Mojolicious::Command undef + Mojolicious::Command::Author::cpanify undef + Mojolicious::Command::Author::generate undef + Mojolicious::Command::Author::generate::app undef + Mojolicious::Command::Author::generate::dockerfile undef + Mojolicious::Command::Author::generate::lite_app undef + Mojolicious::Command::Author::generate::makefile undef + Mojolicious::Command::Author::generate::plugin undef + Mojolicious::Command::Author::inflate undef Mojolicious::Command::cgi undef - Mojolicious::Command::cpanify undef Mojolicious::Command::daemon undef Mojolicious::Command::eval undef - Mojolicious::Command::generate undef - Mojolicious::Command::generate::app undef - Mojolicious::Command::generate::lite_app undef - Mojolicious::Command::generate::makefile undef - Mojolicious::Command::generate::plugin undef Mojolicious::Command::get undef - Mojolicious::Command::inflate undef Mojolicious::Command::prefork undef Mojolicious::Command::psgi undef Mojolicious::Command::routes undef - Mojolicious::Command::test undef Mojolicious::Command::version undef Mojolicious::Commands undef Mojolicious::Controller undef Mojolicious::Lite undef Mojolicious::Plugin undef Mojolicious::Plugin::Config undef - Mojolicious::Plugin::Config::Sandbox undef Mojolicious::Plugin::DefaultHelpers undef Mojolicious::Plugin::EPLRenderer undef Mojolicious::Plugin::EPRenderer undef Mojolicious::Plugin::HeaderCondition undef Mojolicious::Plugin::JSONConfig undef Mojolicious::Plugin::Mount undef - Mojolicious::Plugin::PODRenderer undef + Mojolicious::Plugin::NotYAMLConfig undef Mojolicious::Plugin::TagHelpers undef Mojolicious::Plugins undef Mojolicious::Renderer undef @@ -379,52 +314,55 @@ DISTRIBUTIONS requirements: ExtUtils::MakeMaker 0 IO::Socket::IP 0.37 - JSON::PP 2.27103 - Pod::Simple 3.09 - Time::Local 1.2 - perl 5.010001 - Moo-2.003004 - pathname: H/HA/HAARG/Moo-2.003004.tar.gz + Sub::Util 1.41 + perl 5.016 + Moo-2.005005 + pathname: H/HA/HAARG/Moo-2.005005.tar.gz provides: Method::Generate::Accessor undef Method::Generate::BuildAll undef Method::Generate::Constructor undef Method::Generate::DemolishAll undef - Moo 2.003004 + Moo 2.005005 Moo::HandleMoose undef Moo::HandleMoose::FakeConstructor undef Moo::HandleMoose::FakeMetaClass undef Moo::HandleMoose::_TypeMap undef Moo::Object undef - Moo::Role 2.003004 + Moo::Role 2.005005 Moo::_Utils undef - Moo::_mro undef - Moo::_strictures undef Moo::sification undef oo undef requirements: - Class::Method::Modifiers 1.1 - Devel::GlobalDestruction 0.11 - Exporter 5.57 + Carp 0 + Class::Method::Modifiers 1.10 + Exporter 0 ExtUtils::MakeMaker 0 - Module::Runtime 0.014 - Role::Tiny 2.000004 - Scalar::Util 0 - Sub::Defer 2.003001 - Sub::Quote 2.003001 + Role::Tiny 2.002003 + Scalar::Util 1.00 + Sub::Defer 2.006006 + Sub::Quote 2.006006 perl 5.006 - Role-Tiny-2.000006 - pathname: H/HA/HAARG/Role-Tiny-2.000006.tar.gz + Role-Tiny-2.002004 + pathname: H/HA/HAARG/Role-Tiny-2.002004.tar.gz provides: - Role::Tiny 2.000006 - Role::Tiny::With 2.000006 + Role::Tiny 2.002004 + Role::Tiny::With 2.002004 requirements: Exporter 5.57 perl 5.006 - SQL-Abstract-1.85 - pathname: I/IL/ILMARI/SQL-Abstract-1.85.tar.gz + SQL-Abstract-2.000001 + pathname: M/MS/MSTROUT/SQL-Abstract-2.000001.tar.gz provides: - SQL::Abstract 1.85 + Chunkstrumenter undef + DBIx::Class::SQLMaker::Role::SQLA2Passthrough undef + SQL::Abstract 2.000001 + SQL::Abstract::Formatter undef + SQL::Abstract::Parts undef + SQL::Abstract::Plugin::BangOverrides undef + SQL::Abstract::Plugin::ExtraClauses undef + SQL::Abstract::Reference undef + SQL::Abstract::Role::Plugin undef SQL::Abstract::Test undef SQL::Abstract::Tree undef requirements: @@ -436,26 +374,91 @@ DISTRIBUTIONS Moo 2.000001 Scalar::Util 0 Sub::Quote 2.000001 + Test::Builder::Module 0.84 + Test::Deep 0.101 Text::Balanced 2.00 perl 5.006 - Sub-Exporter-Progressive-0.001013 - pathname: F/FR/FREW/Sub-Exporter-Progressive-0.001013.tar.gz + SQL-Abstract-Pg-1.0 + pathname: S/SR/SRI/SQL-Abstract-Pg-1.0.tar.gz provides: - Sub::Exporter::Progressive 0.001013 + SQL::Abstract::Pg 1.0 requirements: ExtUtils::MakeMaker 0 - Sub-Quote-2.005001 - pathname: H/HA/HAARG/Sub-Quote-2.005001.tar.gz + SQL::Abstract 2.0 + perl 5.016 + Sub-Quote-2.006008 + pathname: H/HA/HAARG/Sub-Quote-2.006008.tar.gz provides: - Sub::Defer 2.005001 - Sub::Quote 2.005001 + Sub::Defer 2.006008 + Sub::Quote 2.006008 requirements: ExtUtils::MakeMaker 0 Scalar::Util 0 perl 5.006 - common-sense-3.74 - pathname: M/ML/MLEHMANN/common-sense-3.74.tar.gz + Test-Deep-1.204 + pathname: R/RJ/RJBS/Test-Deep-1.204.tar.gz + provides: + Test::Deep 1.204 + Test::Deep::All 1.204 + Test::Deep::Any 1.204 + Test::Deep::Array 1.204 + Test::Deep::ArrayEach 1.204 + Test::Deep::ArrayElementsOnly 1.204 + Test::Deep::ArrayLength 1.204 + Test::Deep::ArrayLengthOnly 1.204 + Test::Deep::Blessed 1.204 + Test::Deep::Boolean 1.204 + Test::Deep::Cache 1.204 + Test::Deep::Cache::Simple 1.204 + Test::Deep::Class 1.204 + Test::Deep::Cmp 1.204 + Test::Deep::Code 1.204 + Test::Deep::Hash 1.204 + Test::Deep::HashEach 1.204 + Test::Deep::HashElements 1.204 + Test::Deep::HashKeys 1.204 + Test::Deep::HashKeysOnly 1.204 + Test::Deep::Ignore 1.204 + Test::Deep::Isa 1.204 + Test::Deep::ListMethods 1.204 + Test::Deep::MM 1.204 + Test::Deep::Methods 1.204 + Test::Deep::NoTest 1.204 + Test::Deep::None 1.204 + Test::Deep::Number 1.204 + Test::Deep::Obj 1.204 + Test::Deep::Ref 1.204 + Test::Deep::RefType 1.204 + Test::Deep::Regexp 1.204 + Test::Deep::RegexpMatches 1.204 + Test::Deep::RegexpOnly 1.204 + Test::Deep::RegexpRef 1.204 + Test::Deep::RegexpRefOnly 1.204 + Test::Deep::RegexpVersion 1.204 + Test::Deep::ScalarRef 1.204 + Test::Deep::ScalarRefOnly 1.204 + Test::Deep::Set 1.204 + Test::Deep::Shallow 1.204 + Test::Deep::Stack 1.204 + Test::Deep::String 1.204 + Test::Deep::SubHash 1.204 + Test::Deep::SubHashElements 1.204 + Test::Deep::SubHashKeys 1.204 + Test::Deep::SubHashKeysOnly 1.204 + Test::Deep::SuperHash 1.204 + Test::Deep::SuperHashElements 1.204 + Test::Deep::SuperHashKeys 1.204 + Test::Deep::SuperHashKeysOnly 1.204 + requirements: + ExtUtils::MakeMaker 6.78 + List::Util 1.09 + Scalar::Util 1.09 + Test::Builder 0 + Test::More 0.96 + perl 5.012 + common-sense-3.75 + pathname: M/ML/MLEHMANN/common-sense-3.75.tar.gz provides: - common::sense 3.74 + common::sense 3.75 requirements: ExtUtils::MakeMaker 0 diff --git a/frameworks/Perl/mojolicious/mojolicious.dockerfile b/frameworks/Perl/mojolicious/mojolicious.dockerfile index fe9197e6233..1fec0cc3952 100644 --- a/frameworks/Perl/mojolicious/mojolicious.dockerfile +++ b/frameworks/Perl/mojolicious/mojolicious.dockerfile @@ -1,4 +1,4 @@ -FROM perl:5.26 +FROM perl:5.40 WORKDIR /mojo @@ -19,3 +19,4 @@ ADD ./app.pl ./ EXPOSE 8080 CMD hypnotoad -f /mojo/app.pl + diff --git a/frameworks/Perl/plack/README.md b/frameworks/Perl/plack/README.md index f8172775ec1..d08e9a91a20 100644 --- a/frameworks/Perl/plack/README.md +++ b/frameworks/Perl/plack/README.md @@ -9,4 +9,5 @@ Plack * Twiggy::Prefork * JSON::XS * AnyEvent::DBI -* DBD::mysql +* DBD::MariaDB + diff --git a/frameworks/Perl/plack/app-async.psgi b/frameworks/Perl/plack/app-async.psgi index 93df1c50b2d..b5397d1ca5e 100644 --- a/frameworks/Perl/plack/app-async.psgi +++ b/frameworks/Perl/plack/app-async.psgi @@ -5,7 +5,7 @@ use AnyEvent::DBI; use Unix::Processors; use List::Util qw'min max'; -my @dsn = ('dbi:mysql:database=hello_world;host=tfb-database;port=3306', 'benchmarkdbuser', 'benchmarkdbpass'); +my @dsn = ('dbi:MariaDB:database=hello_world;host=tfb-database;port=3306', 'benchmarkdbuser', 'benchmarkdbpass'); my $query = 'select randomNumber, id from World where id = ?'; sub { @@ -37,3 +37,4 @@ sub { } [404, [qw(Content-Type application/json)], ['not found']] } + diff --git a/frameworks/Perl/plack/app.pl b/frameworks/Perl/plack/app.pl index c1a1172b277..b62eaa6acdc 100644 --- a/frameworks/Perl/plack/app.pl +++ b/frameworks/Perl/plack/app.pl @@ -7,10 +7,10 @@ my @cmd = ( ($opts->{a} # async server - ? [qw'plackup -s Twiggy::Prefork -E production --max-reqs-per-child=0 --backlog 16384 + ? [qw'plackup -s Twiggy::Prefork -E production --max-reqs-per-child=100000 --backlog 16384 --max-workers', $cpus, qw'-l /dev/shm/app.sock -a app-async.psgi'] : [qw'start_server --backlog 16384 --path /dev/shm/app.sock -- - plackup -s Gazelle -E production --max-reqs-per-child 10000000 + plackup -s Gazelle -E production --max-reqs-per-child 100000 --max-workers', $cpus, qw'-a app.psgi']), [qw'nginx -c nginx.conf -p', getcwd] ); diff --git a/frameworks/Perl/plack/app.psgi b/frameworks/Perl/plack/app.psgi index 4f5d8579d38..dc625076660 100644 --- a/frameworks/Perl/plack/app.psgi +++ b/frameworks/Perl/plack/app.psgi @@ -5,9 +5,9 @@ use List::Util qw'min max'; sub { state $dbh = DBI->connect( - 'dbi:mysql:database=hello_world;host=tfb-database;port=3306', + 'dbi:MariaDB:database=hello_world;host=tfb-database;port=3306', 'benchmarkdbuser', 'benchmarkdbpass', - +{ qw'RaiseError 0 PrintError 0 mysql_enable_utf8 1' } + +{ qw'RaiseError 0 PrintError 0' } ) || die $!; state $sth = $dbh->prepare('select id,randomnumber from world where id = ?'); my $env = shift; @@ -25,3 +25,4 @@ sub { } [ 404, [], ['not found']]; } + diff --git a/frameworks/Perl/plack/plack-async.dockerfile b/frameworks/Perl/plack/plack-async.dockerfile index 4af66083005..8ef1ac76b04 100644 --- a/frameworks/Perl/plack/plack-async.dockerfile +++ b/frameworks/Perl/plack/plack-async.dockerfile @@ -1,7 +1,7 @@ FROM perl:latest RUN apt-get update -yqq && apt-get install -yqq nginx -RUN cpanm --notest --no-man-page Plack JSON::XS Unix::Processors DBI DBD::mysql +RUN cpanm --notest --no-man-page Plack JSON::XS Unix::Processors DBI DBD::MariaDB RUN cpanm --notest --no-man-page Cookie::Baker::XS Twiggy::Prefork HTTP::Parser::XS EV AnyEvent::DBI ADD nginx.conf ./ @@ -11,3 +11,4 @@ ADD app-async.psgi ./ EXPOSE 8080 CMD perl app.pl -a + diff --git a/frameworks/Perl/plack/plack.dockerfile b/frameworks/Perl/plack/plack.dockerfile index f2a14dd5b4c..a0391f2f4ca 100644 --- a/frameworks/Perl/plack/plack.dockerfile +++ b/frameworks/Perl/plack/plack.dockerfile @@ -1,7 +1,7 @@ FROM perl:latest RUN apt-get update -yqq && apt-get install -yqq nginx -RUN cpanm --notest --no-man-page Plack JSON::XS Unix::Processors DBI DBD::mysql +RUN cpanm --notest --no-man-page Plack JSON::XS Unix::Processors DBI DBD::MariaDB RUN cpanm --notest --no-man-page Gazelle Cookie::Baker::XS ADD nginx.conf ./ @@ -11,3 +11,4 @@ ADD app.psgi ./ EXPOSE 8080 CMD perl app.pl + diff --git a/frameworks/Perl/web-simple/README.md b/frameworks/Perl/web-simple/README.md index d169c515914..98e8d6a836e 100644 --- a/frameworks/Perl/web-simple/README.md +++ b/frameworks/Perl/web-simple/README.md @@ -7,7 +7,7 @@ # Requirements * Web::Simple -* DBD::mysql +* DBD::MariaDB * Starman (if using Starman as web server) * Plack (for plackup) * nginx (if you want to front Dancer with nginx, nginx.conf provided) @@ -21,3 +21,4 @@ Something along the lines of if you want to front it with nginx, otherwise plackup -E production -s Starman --port=8080 --workers=8 -a ./app.pl + diff --git a/frameworks/Perl/web-simple/app.pl b/frameworks/Perl/web-simple/app.pl index d2db3e11299..2447c765c01 100755 --- a/frameworks/Perl/web-simple/app.pl +++ b/frameworks/Perl/web-simple/app.pl @@ -4,7 +4,7 @@ use DBI; sub get_database_handle { - DBI->connect_cached('dbi:mysql:database=hello_world;host=tfb-database', 'benchmarkdbuser', 'benchmarkdbpass', { RaiseError => 1 }); + DBI->connect_cached('dbi:MariaDB:database=hello_world;host=tfb-database', 'benchmarkdbuser', 'benchmarkdbpass', { RaiseError => 1 }); } sub dispatch_request { @@ -54,3 +54,4 @@ sub dispatch_request { } __PACKAGE__->run_if_script; + diff --git a/frameworks/Perl/web-simple/benchmark_config.json b/frameworks/Perl/web-simple/benchmark_config.json index 629b19df0c1..6725005d211 100644 --- a/frameworks/Perl/web-simple/benchmark_config.json +++ b/frameworks/Perl/web-simple/benchmark_config.json @@ -19,7 +19,8 @@ "display_name": "web-simple", "notes": "", "versus": "", - "tags": ["broken"] + "tags": [] } }] } + diff --git a/frameworks/Perl/web-simple/web-simple.dockerfile b/frameworks/Perl/web-simple/web-simple.dockerfile index f3b83b7a7c0..5a2678bfe1b 100644 --- a/frameworks/Perl/web-simple/web-simple.dockerfile +++ b/frameworks/Perl/web-simple/web-simple.dockerfile @@ -1,4 +1,4 @@ -FROM perl:5.26 +FROM perl:5.40 RUN apt-get update -yqq && apt-get install -yqq nginx @@ -8,7 +8,7 @@ RUN cpanm --notest --no-man-page \ JSON JSON::XS IO::Socket::IP IO::Socket::SSL \ Web::Simple@0.033 \ DBI@1.637 \ - DBD::mysql@4.043 \ + DBD::MariaDB@1.23 \ Plack@1.0044 \ Starman@0.4014 \ JSON::XS@3.04 @@ -21,4 +21,6 @@ EXPOSE 8080 CMD nginx -c /simple/nginx.conf && \ plackup -E production -s Starman --workers=$(nproc) \ + --max-requests=100000 \ -l /tmp/perl-simple.sock -a /simple/app.pl + diff --git a/frameworks/Python/aiohttp/requirements.txt b/frameworks/Python/aiohttp/requirements.txt index d0caf5f25e7..444a284f816 100644 --- a/frameworks/Python/aiohttp/requirements.txt +++ b/frameworks/Python/aiohttp/requirements.txt @@ -1,4 +1,4 @@ -aiohttp==3.9.4 +aiohttp==3.10.2 asyncpg==0.25.0 cchardet==2.1.7 gunicorn==20.1 diff --git a/frameworks/Python/api_hour/requirements.txt b/frameworks/Python/api_hour/requirements.txt index 81b923f4005..19dcab407ab 100644 --- a/frameworks/Python/api_hour/requirements.txt +++ b/frameworks/Python/api_hour/requirements.txt @@ -1,4 +1,4 @@ -aiohttp==3.9.4 +aiohttp==3.10.2 -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/benchmark_config.json b/frameworks/Python/bottle/benchmark_config.json index e37735b8353..c895cf1e4e7 100644 --- a/frameworks/Python/bottle/benchmark_config.json +++ b/frameworks/Python/bottle/benchmark_config.json @@ -45,7 +45,8 @@ "database_os": "Linux", "display_name": "Bottle", "notes": "PyPy2", - "versus": "wsgi" + "versus": "wsgi", + "tags": ["broken"] }, "raw": { "db_url": "/raw-db", diff --git a/frameworks/Python/bottle/bottle-raw.dockerfile b/frameworks/Python/bottle/bottle-raw.dockerfile index 24a59598829..8ba573a7a2b 100644 --- a/frameworks/Python/bottle/bottle-raw.dockerfile +++ b/frameworks/Python/bottle/bottle-raw.dockerfile @@ -1,4 +1,4 @@ -FROM python:3.6.6-stretch +FROM python:3.9-bookworm WORKDIR /bottle COPY views views diff --git a/frameworks/Python/bottle/bottle.dockerfile b/frameworks/Python/bottle/bottle.dockerfile index 24a59598829..8ba573a7a2b 100644 --- a/frameworks/Python/bottle/bottle.dockerfile +++ b/frameworks/Python/bottle/bottle.dockerfile @@ -1,4 +1,4 @@ -FROM python:3.6.6-stretch +FROM python:3.9-bookworm WORKDIR /bottle COPY views views diff --git a/frameworks/Python/bottle/requirements.txt b/frameworks/Python/bottle/requirements.txt index 25c2d97e91a..7f6135dbd5b 100644 --- a/frameworks/Python/bottle/requirements.txt +++ b/frameworks/Python/bottle/requirements.txt @@ -1,6 +1,6 @@ -bottle==0.12.19 +bottle==0.12.25 bottle-sqlalchemy==0.4.3 -greenlet==0.4.14 +greenlet==0.4.17 gunicorn==19.9.0 meinheld==1.0.2 mysqlclient==1.3.12 diff --git a/frameworks/Python/emmett/app.py b/frameworks/Python/emmett/app.py index 50a942ce7ad..7d012fe70b3 100644 --- a/frameworks/Python/emmett/app.py +++ b/frameworks/Python/emmett/app.py @@ -32,7 +32,7 @@ def _serialize(self, row): app.config.db.user = 'benchmarkdbuser' app.config.db.password = 'benchmarkdbpass' app.config.db.database = 'hello_world' -app.config.db.pool_size = 10 +app.config.db.pool_size = 16 db = Database(app) db.define_models(World, Fortune) diff --git a/frameworks/Python/emmett/requirements.txt b/frameworks/Python/emmett/requirements.txt index 2c329705017..1621f272390 100644 --- a/frameworks/Python/emmett/requirements.txt +++ b/frameworks/Python/emmett/requirements.txt @@ -1,2 +1,2 @@ -emmett[orjson]>=2.5.3,<2.6.0 -psycopg2-binary==2.9.5 +emmett[orjson]>=2.6.0,<2.7.0 +psycopg2-binary==2.9.9 diff --git a/frameworks/Python/emmett/run.py b/frameworks/Python/emmett/run.py index 42679101ea0..aad30c549c6 100644 --- a/frameworks/Python/emmett/run.py +++ b/frameworks/Python/emmett/run.py @@ -1,19 +1,20 @@ import multiprocessing -from emmett.server import run +from emmett_core.server import run if __name__ == "__main__": - cpus = multiprocessing.cpu_count() + workers = multiprocessing.cpu_count() run( "rsgi", ("app", "app"), host="0.0.0.0", port=8080, - workers=cpus, + workers=workers, backlog=16384, - threading_mode='runtime', + threading_mode="runtime", + http="1", enable_websockets=False, log_level="warn" ) diff --git a/frameworks/Python/emmett55/README.md b/frameworks/Python/emmett55/README.md new file mode 100644 index 00000000000..0e36619cfc8 --- /dev/null +++ b/frameworks/Python/emmett55/README.md @@ -0,0 +1,26 @@ +# Emmett55 Benchmark Test + +This is the Emmett55 portion of a [benchmarking tests suite](../../) comparing a variety of web development platforms. + +The information below is specific to Emmett55. For further guidance, review the [documentation](https://github.com/TechEmpower/FrameworkBenchmarks/wiki). + +Also note that there is additional information provided in the [Python README](../). + +## Description + +[Emmett55](https://github.com/emmett-framework/emmett55) is a Python asyncIO micro web framework. + +## Test Paths & Source + +* [JSON Serialization](app.py): "/json" +* [Single Database Query](app.py): "/db" +* [Multiple Database Queries](app.py): "queries?queries=#" +* [Fortunes](app.py): "/fortunes" +* [Database Updates](app.py): "updates?queries=#" +* [Plaintext](app.py): "/plaintext" + +*Replace # with an actual number.* + +### Resources + +* [Github repository](https://github.com/emmett-framework/emmett55) diff --git a/frameworks/Python/emmett55/app.py b/frameworks/Python/emmett55/app.py new file mode 100644 index 00000000000..e417e8f75fa --- /dev/null +++ b/frameworks/Python/emmett55/app.py @@ -0,0 +1,129 @@ +import os +from operator import itemgetter +from random import randint, sample + +import asyncpg +from emmett55 import App, Pipe, current, request, response +from emmett55.extensions import Extension, Signals, listen_signal +from emmett55.tools import service +from renoir import Renoir + + +class AsyncPG(Extension): + __slots__ = ["pool"] + + def on_load(self): + self.pool = None + self.pipe = AsyncPGPipe(self) + + async def build_pool(self): + self.pool = await asyncpg.create_pool( + user=os.getenv('PGUSER', 'benchmarkdbuser'), + password=os.getenv('PGPASS', 'benchmarkdbpass'), + database='hello_world', + host='tfb-database', + port=5432, + min_size=16, + max_size=16, + max_queries=64_000_000_000, + max_inactive_connection_lifetime=0 + ) + + @listen_signal(Signals.after_loop) + def _init_pool(self, loop): + loop.run_until_complete(self.build_pool()) + + +class AsyncPGPipe(Pipe): + __slots__ = ["ext"] + + def __init__(self, ext): + self.ext = ext + + async def open(self): + conn = current._db_conn = self.ext.pool.acquire() + current.db = await conn.__aenter__() + + async def close(self): + await current._db_conn.__aexit__() + + +app = App(__name__) +app.config.handle_static = False +templates = Renoir() + +db_ext = app.use_extension(AsyncPG) + +SQL_SELECT = 'SELECT "randomnumber", "id" FROM "world" WHERE id = $1' +SQL_UPDATE = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' +ROW_ADD = [0, 'Additional fortune added at request time.'] +sort_key = itemgetter(1) + + +@app.route() +@service.json +async def json(): + return {'message': 'Hello, World!'} + + +@app.route("/db", pipeline=[db_ext.pipe]) +@service.json +async def get_random_world(): + row_id = randint(1, 10000) + number = await current.db.fetchval(SQL_SELECT, row_id) + return {'id': row_id, 'randomNumber': number} + + +def get_qparam(): + try: + rv = int(request.query_params.queries or 1) + except ValueError: + return 1 + if rv < 1: + return 1 + if rv > 500: + return 500 + return rv + + +@app.route("/queries", pipeline=[db_ext.pipe]) +@service.json +async def get_random_worlds(): + num_queries = get_qparam() + row_ids = sample(range(1, 10000), num_queries) + worlds = [] + statement = await current.db.prepare(SQL_SELECT) + for row_id in row_ids: + number = await statement.fetchval(row_id) + worlds.append({'id': row_id, 'randomNumber': number}) + return worlds + + +@app.route(pipeline=[db_ext.pipe], output='str') +async def fortunes(): + response.content_type = "text/html; charset=utf-8" + fortunes = await current.db.fetch('SELECT * FROM Fortune') + fortunes.append(ROW_ADD) + fortunes.sort(key=sort_key) + return templates.render("templates/fortunes.html", {"fortunes": fortunes}) + + +@app.route(pipeline=[db_ext.pipe]) +@service.json +async def updates(): + num_queries = get_qparam() + updates = list(zip( + sample(range(1, 10000), num_queries), + sorted(sample(range(1, 10000), num_queries)) + )) + worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates] + statement = await current.db.prepare(SQL_SELECT) + for row_id, _ in updates: + await statement.fetchval(row_id) + await current.db.executemany(SQL_UPDATE, updates) + return worlds + + +@app.route(output='bytes') +async def plaintext(): + return b'Hello, World!' diff --git a/frameworks/Python/emmett55/benchmark_config.json b/frameworks/Python/emmett55/benchmark_config.json new file mode 100644 index 00000000000..e842529efcb --- /dev/null +++ b/frameworks/Python/emmett55/benchmark_config.json @@ -0,0 +1,27 @@ +{ + "framework": "emmett55", + "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": "Micro", + "database": "Postgres", + "framework": "Emmett55", + "language": "Python", + "orm": "Raw", + "platform": "RSGI", + "webserver": "granian", + "os": "Linux", + "database_os": "Linux", + "display_name": "Emmett55", + "notes": "CPython 3.7", + "versus": "uvicorn" + } + }] +} diff --git a/frameworks/Python/emmett55/config.toml b/frameworks/Python/emmett55/config.toml new file mode 100644 index 00000000000..11586c4e530 --- /dev/null +++ b/frameworks/Python/emmett55/config.toml @@ -0,0 +1,19 @@ +[framework] +name = "emmett55" + +[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 = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "RSGI" +webserver = "granian" +versus = "uvicorn" diff --git a/frameworks/Python/emmett55/emmett55.dockerfile b/frameworks/Python/emmett55/emmett55.dockerfile new file mode 100644 index 00000000000..49438fd442d --- /dev/null +++ b/frameworks/Python/emmett55/emmett55.dockerfile @@ -0,0 +1,11 @@ +FROM python:3.11-slim + +ADD ./ /emmett55 + +WORKDIR /emmett55 + +RUN pip install --no-cache-dir -r /emmett55/requirements.txt + +EXPOSE 8080 + +CMD python run.py diff --git a/frameworks/Python/emmett55/requirements.txt b/frameworks/Python/emmett55/requirements.txt new file mode 100644 index 00000000000..ecf2313a108 --- /dev/null +++ b/frameworks/Python/emmett55/requirements.txt @@ -0,0 +1,3 @@ +asyncpg==0.29.0 +emmett55[orjson]>=1.0.0,<1.1.0 +renoir==1.8.0 diff --git a/frameworks/Python/emmett55/run.py b/frameworks/Python/emmett55/run.py new file mode 100644 index 00000000000..aad30c549c6 --- /dev/null +++ b/frameworks/Python/emmett55/run.py @@ -0,0 +1,20 @@ +import multiprocessing + +from emmett_core.server import run + + +if __name__ == "__main__": + workers = multiprocessing.cpu_count() + + run( + "rsgi", + ("app", "app"), + host="0.0.0.0", + port=8080, + workers=workers, + backlog=16384, + threading_mode="runtime", + http="1", + enable_websockets=False, + log_level="warn" + ) diff --git a/frameworks/Python/emmett55/templates/fortunes.html b/frameworks/Python/emmett55/templates/fortunes.html new file mode 100644 index 00000000000..c64ff16ec6f --- /dev/null +++ b/frameworks/Python/emmett55/templates/fortunes.html @@ -0,0 +1,20 @@ + + + + Fortunes + + + + + + + + {{ for fortune in fortunes: }} + + + + + {{ pass }} +
idmessage
{{ =fortune[0] }}{{ =fortune[1] }}
+ + diff --git a/frameworks/Python/granian/requirements.txt b/frameworks/Python/granian/requirements.txt index e1b7c479119..90c02f2c8d4 100644 --- a/frameworks/Python/granian/requirements.txt +++ b/frameworks/Python/granian/requirements.txt @@ -1,4 +1,4 @@ asyncpg==0.29.0 -granian>=1.4.2,<1.5.0 +granian>=1.6.0,<1.7.0 jinja2==3.1.4 orjson==3.10.2 diff --git a/frameworks/Python/granian/run.py b/frameworks/Python/granian/run.py index acdad59a542..ef0724fad2f 100644 --- a/frameworks/Python/granian/run.py +++ b/frameworks/Python/granian/run.py @@ -8,16 +8,23 @@ interface = sys.argv[1] threading_mode = sys.argv[2] workers = multiprocessing.cpu_count() - if threading_mode == "workers": + + if interface == "rsgi": + #: split cores between the two loops workers = round(workers / 2) + blocking_threads = None + if interface == "wsgi": + #: we don't run any I/O in WSGI benches + blocking_threads = 1 + Granian( f"app_{interface}:main", address="0.0.0.0", port=8080, workers=workers, threading_mode=threading_mode, - blocking_threads=1, + blocking_threads=blocking_threads, backlog=16384, interface=interface, http="1", diff --git a/frameworks/Python/morepath/requirements.txt b/frameworks/Python/morepath/requirements.txt index b1ff4b72a3a..8f58488b64e 100644 --- a/frameworks/Python/morepath/requirements.txt +++ b/frameworks/Python/morepath/requirements.txt @@ -12,6 +12,6 @@ pony==0.7.1 psycopg2==2.7.5 reg==0.11 repoze.lru==0.6 -WebOb==1.7.2 +WebOb==1.8.8 -e . diff --git a/frameworks/Python/panther/README.md b/frameworks/Python/panther/README.md index c1edfd4f5b7..f57bc888b7e 100644 --- a/frameworks/Python/panther/README.md +++ b/frameworks/Python/panther/README.md @@ -31,6 +31,6 @@ All tests are implemented in a single file ([app.py](app.py)). ## Resources -* [GitHub](https://github.com/AliRn76/Panther) -* [Documentation](https://pantherpy.github.io) -* [PyPI](https://pypi.org/project/panther) +* GitHub -> [GitHub.com/AliRn76/Panther](https://github.com/AliRn76/Panther) +* Documentation -> [PantherPy.GitHub.io](https://pantherpy.github.io) +* PyPI -> [PyPI.org/project/Panther](https://pypi.org/project/panther) diff --git a/frameworks/Python/panther/app.py b/frameworks/Python/panther/app.py index b3f712cd979..0b20d44413c 100644 --- a/frameworks/Python/panther/app.py +++ b/frameworks/Python/panther/app.py @@ -1,11 +1,12 @@ import multiprocessing -import os +from pathlib import Path from random import randint, sample import asyncpg import jinja2 from panther import Panther from panther.app import API +from panther.events import Event from panther.request import Request from panther.response import Response, PlainTextResponse, HTMLResponse @@ -18,6 +19,7 @@ pool = None +@Event.startup async def create_db_pool(): global pool pool = await asyncpg.create_pool( @@ -31,18 +33,13 @@ async def create_db_pool(): ) +@Event.shutdown async def clean_db_pool(): await pool.close() -def load_fortunes_template(): - path = os.path.join('templates', 'fortune.html') - with open(path, 'r') as template_file: - template_text = template_file.read() - return jinja2.Template(template_text) - - -fortune_template = load_fortunes_template() +with Path('templates/fortune.html').open() as f: + fortune_template = jinja2.Template(f.read()) def get_num_queries(request): @@ -99,17 +96,16 @@ async def fortunes(): @API() async def database_updates(request: Request): num_queries = get_num_queries(request) - ids = sorted(sample(range(1, 10000 + 1), num_queries)) - numbers = sorted(sample(range(1, 10000), num_queries)) - updates = list(zip(ids, numbers)) + updates = list(zip( + sample(range(1, 10000), num_queries), + sorted(sample(range(1, 10000), num_queries)) + )) - worlds = [ - {'id': row_id, 'randomNumber': number} for row_id, number in updates - ] + worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates] async with pool.acquire() as connection: statement = await connection.prepare(READ_ROW_SQL) - for row_id, _ in updates: + for _, row_id in updates: await statement.fetchval(row_id) await connection.executemany(WRITE_ROW_SQL, updates) return Response(data=worlds) @@ -129,4 +125,4 @@ async def plaintext(): 'plaintext': plaintext, } -app = Panther(__name__, configs=__name__, urls=url_routing, startup=create_db_pool, shutdown=clean_db_pool) +app = Panther(__name__, configs=__name__, urls=url_routing) diff --git a/frameworks/Python/panther/requirements.txt b/frameworks/Python/panther/requirements.txt index 4a494ed3f7a..691d573162f 100644 --- a/frameworks/Python/panther/requirements.txt +++ b/frameworks/Python/panther/requirements.txt @@ -1,11 +1,5 @@ -panther==3.2.1 - -cython==3.0.6 -jinja2==3.1.4 - +panther==4.3.1 +cython==3.0.11 asyncpg==0.29.0 - -gunicorn==22.0.0 -uvicorn==0.24.0 -uvloop==0.19.0 -httptools==0.6.1 +gunicorn==23.0.0 +uvloop==0.20.0 diff --git a/frameworks/Python/robyn/app-const.py b/frameworks/Python/robyn/app-const.py index e082ff1b129..ceec8488829 100755 --- a/frameworks/Python/robyn/app-const.py +++ b/frameworks/Python/robyn/app-const.py @@ -1,7 +1,7 @@ import multiprocessing import os -from robyn import Response, Robyn, jsonify +from robyn import Response, Robyn from robyn.argument_parser import Config @@ -9,7 +9,7 @@ class SpecialConfig(Config): def __init__(self): super().__init__() self.workers = 2 - self.processes = (os.cpu_count() * 2) + 1 + self.processes = ( os.cpu_count() * 2 ) + 1 self.log_level = "WARN" @@ -22,16 +22,12 @@ def plaintext() -> str: @app.get("/json", const=True) -def json() -> str: - return Response( - status_code=200, - description=jsonify({"message": "Hello, world!"}), - headers={"Content-Type": "application/json"} - ) - +def json() -> dict: + return { + "message": "Hello, world!" + } if __name__ == "__main__": app.add_response_header("Server", "Robyn") - app.start(host="0.0.0.0", port=8080) diff --git a/frameworks/Python/robyn/app.py b/frameworks/Python/robyn/app.py index 798b67026e2..bbbb037868a 100755 --- a/frameworks/Python/robyn/app.py +++ b/frameworks/Python/robyn/app.py @@ -1,7 +1,7 @@ import multiprocessing import os -from robyn import Response, Robyn, jsonify +from robyn import Response, Robyn from robyn.argument_parser import Config @@ -9,7 +9,7 @@ class SpecialConfig(Config): def __init__(self): super().__init__() self.workers = 2 - self.processes = (os.cpu_count() * 2) + 1 + self.processes = ( os.cpu_count() * 2 ) + 1 self.log_level = "WARN" @@ -22,13 +22,10 @@ def plaintext() -> str: @app.get("/json") -def json() -> str: - return Response( - status_code=200, - description=jsonify({"message": "Hello, world!"}), - headers={"Content-Type": "application/json"} - ) - +def json() -> dict: + return { + "message": "Hello, world!" + } if __name__ == "__main__": app.add_response_header("Server", "Roby1n") diff --git a/frameworks/Python/robyn/benchmark_config.json b/frameworks/Python/robyn/benchmark_config.json index 3d8c6baa8a3..46cd72fe4c6 100755 --- a/frameworks/Python/robyn/benchmark_config.json +++ b/frameworks/Python/robyn/benchmark_config.json @@ -38,4 +38,5 @@ } } ] -} \ No newline at end of file +} + diff --git a/frameworks/Python/robyn/requirements-const.txt b/frameworks/Python/robyn/requirements-const.txt index 0056c1d2876..484d4b7704f 100644 --- a/frameworks/Python/robyn/requirements-const.txt +++ b/frameworks/Python/robyn/requirements-const.txt @@ -1,2 +1,2 @@ uvloop==0.19.0 -robyn==0.45.0 +robyn==0.62.0 diff --git a/frameworks/Python/robyn/requirements.txt b/frameworks/Python/robyn/requirements.txt index 0056c1d2876..484d4b7704f 100644 --- a/frameworks/Python/robyn/requirements.txt +++ b/frameworks/Python/robyn/requirements.txt @@ -1,2 +1,2 @@ uvloop==0.19.0 -robyn==0.45.0 +robyn==0.62.0 diff --git a/frameworks/Python/robyn/robyn-const.dockerfile b/frameworks/Python/robyn/robyn-const.dockerfile index a12c64e248f..9429ff37376 100644 --- a/frameworks/Python/robyn/robyn-const.dockerfile +++ b/frameworks/Python/robyn/robyn-const.dockerfile @@ -8,4 +8,4 @@ RUN pip3 install -r /robyn/requirements-const.txt EXPOSE 8080 -CMD ["python", "app-const.py", "--log-level", "warn"]] +CMD ["python", "app-const.py", "--log-level", "warn"] diff --git a/frameworks/Python/sanic/app.py b/frameworks/Python/sanic/app.py index 47b374ce9a3..4070503205d 100644 --- a/frameworks/Python/sanic/app.py +++ b/frameworks/Python/sanic/app.py @@ -11,6 +11,8 @@ import sanic from sanic import response +from orjson import dumps + logger = getLogger(__name__) @@ -41,23 +43,26 @@ def get_num_queries(queries): return query_count -connection_pool = None sort_fortunes_key = itemgetter(1) template = load_fortunes_template() -app = sanic.Sanic(name=__name__) +app = sanic.Sanic(name=__name__, dumps=dumps) @app.listener('before_server_start') async def setup_database(app, loop): - global connection_pool - connection_pool = await asyncpg.create_pool( - user=os.getenv('PGUSER', 'benchmarkdbuser'), - password=os.getenv('PGPASS', 'benchmarkdbpass'), - database='hello_world', - host='tfb-database', - port=5432 - ) + app.ctx.pool = await asyncpg.create_pool( + user=os.getenv('PGUSER', 'benchmarkdbuser'), + password=os.getenv('PGPASS', 'benchmarkdbpass'), + database='hello_world', + host='tfb-database', + port=5432 + ) + + +@app.listener('after_server_stop') +async def close_database(app, loop): + app.ctx.pool.close() @app.get('/json') @@ -69,7 +74,7 @@ def json_view(request): async def single_database_query_view(request): row_id = randint(1, 10000) - async with connection_pool.acquire() as connection: + async with request.app.ctx.pool.acquire() as connection: number = await connection.fetchval(READ_ROW_SQL, row_id) return response.json( @@ -84,7 +89,7 @@ async def multiple_database_queries_view(request): row_ids = sample(range(1, 10000), num_queries) worlds = [] - async with connection_pool.acquire() as connection: + async with request.app.ctx.pool.acquire() as connection: statement = await connection.prepare(READ_ROW_SQL) for row_id in row_ids: number = await statement.fetchval(row_id) @@ -100,7 +105,7 @@ async def multiple_database_queries_view(request): @app.get('/fortunes') async def fortunes_view(request): - async with connection_pool.acquire() as connection: + async with request.app.ctx.pool.acquire() as connection: fortunes = await connection.fetch('SELECT * FROM Fortune') fortunes.append(ADDITIONAL_ROW) @@ -112,22 +117,21 @@ async def fortunes_view(request): @app.get('/updates') async def database_updates_view(request): - worlds = [] - updates = set() queries = request.args.get('queries', 1) + num_queries = get_num_queries(queries) + # To avoid deadlock + ids = sorted(sample(range(1, 10000 + 1), num_queries)) + numbers = sorted(sample(range(1, 10000), num_queries)) + updates = list(zip(ids, numbers)) - async with connection_pool.acquire() as connection: - statement = await connection.prepare(READ_ROW_SQL_TO_UPDATE) - - for row_id in sample(range(1, 10000), get_num_queries(queries)): - record = await statement.fetchrow(row_id) - world = dict( - id=record['id'], randomNumber=record['randomnumber'] - ) - world['randomNumber'] = randint(1, 10000) - worlds.append(world) - updates.add((world['id'], world['randomNumber'])) + worlds = [ + {"id": row_id, "randomNumber": number} for row_id, number in updates + ] + async with request.app.ctx.pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id, _ in updates: + await statement.fetchval(row_id) await connection.executemany(WRITE_ROW_SQL, updates) return response.json(worlds, headers=get_headers()) diff --git a/frameworks/Python/sanic/benchmark_config.json b/frameworks/Python/sanic/benchmark_config.json index b1bdd529d60..b3c6799b42b 100644 --- a/frameworks/Python/sanic/benchmark_config.json +++ b/frameworks/Python/sanic/benchmark_config.json @@ -23,8 +23,7 @@ "database_os": "Linux", "display_name": "Sanic", "notes": "", - "versus": "None", - "tags": ["broken"] + "versus": "None" } } ] diff --git a/frameworks/Python/sanic/requirements.txt b/frameworks/Python/sanic/requirements.txt index 5b4738e167d..ff8b4afd8a9 100644 --- a/frameworks/Python/sanic/requirements.txt +++ b/frameworks/Python/sanic/requirements.txt @@ -1,4 +1,5 @@ -asyncpg==0.25.0 +asyncpg==0.29.0 Jinja2==3.1.4 -sanic==22.6.1 -uvloop==0.16.0 +sanic==24.6.0 +uvloop==0.20.0 +orjson==3.10.7 \ No newline at end of file diff --git a/frameworks/Python/sanic/sanic.dockerfile b/frameworks/Python/sanic/sanic.dockerfile index 9619237ed21..d12ebcb391a 100644 --- a/frameworks/Python/sanic/sanic.dockerfile +++ b/frameworks/Python/sanic/sanic.dockerfile @@ -1,8 +1,8 @@ -FROM python:3.8 +FROM python:3.12 ADD ./requirements.txt /sanic/requirements.txt -RUN pip3 install cython==0.29.13 && \ +RUN pip3 install cython==3.0.11 && \ pip3 install -r /sanic/requirements.txt ADD ./ /sanic diff --git a/frameworks/Python/starlette/requirements.txt b/frameworks/Python/starlette/requirements.txt index ef5a1748ca4..47b27a87e9a 100644 --- a/frameworks/Python/starlette/requirements.txt +++ b/frameworks/Python/starlette/requirements.txt @@ -6,6 +6,6 @@ Jinja2==3.1.4 MarkupSafe==2.1.1 python-dotenv==0.20.0 PyYAML==6.0 -starlette==0.36.2 +starlette==0.40.0 uvicorn==0.20.0 uvloop==0.17.0 diff --git a/frameworks/Ruby/agoo/agoo.dockerfile b/frameworks/Ruby/agoo/agoo.dockerfile index bb4e363b687..c8c08141281 100644 --- a/frameworks/Ruby/agoo/agoo.dockerfile +++ b/frameworks/Ruby/agoo/agoo.dockerfile @@ -11,7 +11,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 WORKDIR /rack diff --git a/frameworks/Ruby/grape/grape-unicorn.dockerfile b/frameworks/Ruby/grape/grape-unicorn.dockerfile index 3cc4c7d2d37..f805021f71e 100644 --- a/frameworks/Ruby/grape/grape-unicorn.dockerfile +++ b/frameworks/Ruby/grape/grape-unicorn.dockerfile @@ -2,6 +2,11 @@ FROM ruby:3.3 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 + RUN apt-get update -yqq && apt-get install -yqq nginx ADD ./ /grape diff --git a/frameworks/Ruby/grape/grape.dockerfile b/frameworks/Ruby/grape/grape.dockerfile index 5beaf426ddd..99898d43e65 100644 --- a/frameworks/Ruby/grape/grape.dockerfile +++ b/frameworks/Ruby/grape/grape.dockerfile @@ -2,6 +2,11 @@ FROM ruby:3.3 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 ./ /grape WORKDIR /grape diff --git a/frameworks/Ruby/h2o_mruby/h2o_mruby.dockerfile b/frameworks/Ruby/h2o_mruby/h2o_mruby.dockerfile index 7cc59cf699f..cdfbf5811b2 100644 --- a/frameworks/Ruby/h2o_mruby/h2o_mruby.dockerfile +++ b/frameworks/Ruby/h2o_mruby/h2o_mruby.dockerfile @@ -4,7 +4,7 @@ ARG H2O_PREFIX=/opt/h2o FROM "ubuntu:${UBUNTU_VERSION}" AS compile -ARG H2O_VERSION=18b175f71ede08b50d3e5ae8303dacef3ea510fc +ARG H2O_VERSION=c54c63285b52421da2782f028022647fc2ea3dd1 ARG DEBIAN_FRONTEND=noninteractive ARG H2O_PREFIX diff --git a/frameworks/Ruby/hanami/app/actions/db/index.rb b/frameworks/Ruby/hanami/app/actions/db/index.rb index 3751a8d0621..c32ec0f1df0 100644 --- a/frameworks/Ruby/hanami/app/actions/db/index.rb +++ b/frameworks/Ruby/hanami/app/actions/db/index.rb @@ -2,7 +2,7 @@ module HelloWorld module Actions - module Db + module DB class Index < HelloWorld::Action QUERY_RANGE = 1..10_000 # range of IDs in the Fortune DB include Deps["persistence.rom"] diff --git a/frameworks/Ruby/hanami/hanami.dockerfile b/frameworks/Ruby/hanami/hanami.dockerfile index 01972160167..6ba8e69f3e6 100644 --- a/frameworks/Ruby/hanami/hanami.dockerfile +++ b/frameworks/Ruby/hanami/hanami.dockerfile @@ -5,7 +5,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 WORKDIR /hanami diff --git a/frameworks/Ruby/rack-sequel/Gemfile b/frameworks/Ruby/rack-sequel/Gemfile index fb4b4ad65d8..12b60ef57cf 100644 --- a/frameworks/Ruby/rack-sequel/Gemfile +++ b/frameworks/Ruby/rack-sequel/Gemfile @@ -1,19 +1,21 @@ source 'https://rubygems.org' +gem 'base64' # required by passenger on Ruby 3.4 gem 'json', '~> 2.0' -gem 'passenger', '~> 6.0', :platforms=>[:ruby, :mswin], :require=>false -gem 'puma', '~> 6.4', :require=>false +gem 'oj', '~> 3.14', platforms: %i[ruby mswin] +gem 'passenger', '~> 6.0', platforms: [:ruby, :mswin], require: false +gem 'puma', '~> 6.4', require: false gem 'sequel', '~> 5.0' gem 'rack', '~> 3.0' -gem 'unicorn', '~> 6.1', :platforms=>[:ruby, :mswin], :require=>false +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 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 d68de1f48d7..6fc3a787975 100644 --- a/frameworks/Ruby/rack-sequel/hello_world.rb +++ b/frameworks/Ruby/rack-sequel/hello_world.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'oj' +Oj.mimic_JSON + # Our Rack application to be executed by rackup class HelloWorld DEFAULT_HEADERS = {}.tap do |h| @@ -91,19 +94,19 @@ def call(env) case env['PATH_INFO'] when '/json' # Test type 1: JSON serialization - [JSON_TYPE, JSON.fast_generate(message: 'Hello, World!')] + [JSON_TYPE, { message: 'Hello, World!' }.to_json] when '/db' # Test type 2: Single database query - [JSON_TYPE, JSON.fast_generate(db)] + [JSON_TYPE, db.to_json] when '/queries' # Test type 3: Multiple database queries - [JSON_TYPE, JSON.fast_generate(queries(env))] + [JSON_TYPE, queries(env).to_json] when '/fortunes' # Test type 4: Fortunes [HTML_TYPE, fortunes] when '/updates' # Test type 5: Database updates - [JSON_TYPE, JSON.fast_generate(updates(env))] + [JSON_TYPE, updates(env).to_json] when '/plaintext' # Test type 6: Plaintext [PLAINTEXT_TYPE, 'Hello, World!'] diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-passenger-mri.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-passenger-mri.dockerfile index 7b82353185d..c0de276d2ce 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 @@ -9,7 +9,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile 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 78053b6f376..0346a44eb1b 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 @@ -9,7 +9,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile 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 a3aca7b79bc..37ce2662e7b 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-postgres-unicorn-mri.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-postgres-unicorn-mri.dockerfile @@ -9,7 +9,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile index 8033d74c31a..37634239c37 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile @@ -9,7 +9,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile index 6df05c0c2b6..67ef3768e71 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile @@ -9,7 +9,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile diff --git a/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile index 470d92aa4b3..2c7ee155a56 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile @@ -9,7 +9,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile diff --git a/frameworks/Ruby/rack/Gemfile b/frameworks/Ruby/rack/Gemfile index af0fc5b1d04..ab7f29130b7 100644 --- a/frameworks/Ruby/rack/Gemfile +++ b/frameworks/Ruby/rack/Gemfile @@ -4,16 +4,25 @@ source 'https://rubygems.org' gem 'rack', '~> 3.0' gem 'connection_pool', '~> 2.4' -gem 'falcon', '~> 0.47', platforms: %i[ruby mswin] 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 'pg', '~> 1.5', platforms: %i[ruby mswin] -gem 'puma', '~> 6.4' gem 'sequel' gem 'sequel_pg', platforms: %i[ruby mswin] gem 'tzinfo-data', '1.2023.3' -gem 'unicorn', '~> 6.1', platforms: %i[ruby mswin], require: false + +group :falcon do + gem 'falcon', '~> 0.47', platforms: %i[ruby mswin] +end + +group :puma do + gem 'puma', '~> 6.4' +end + +group :unicorn do + gem 'unicorn', '~> 6.1', platforms: %i[ruby mswin] +end group :development do gem 'rack-test' diff --git a/frameworks/Ruby/rack/Gemfile.lock b/frameworks/Ruby/rack/Gemfile.lock index 75e57b3ea0e..ee579e13085 100644 --- a/frameworks/Ruby/rack/Gemfile.lock +++ b/frameworks/Ruby/rack/Gemfile.lock @@ -2,37 +2,36 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - async (2.11.0) + async (2.12.1) console (~> 1.25, >= 1.25.2) fiber-annotation - io-event (~> 1.5, >= 1.5.1) - timers (~> 4.1) + io-event (~> 1.6, >= 1.6.5) async-container (0.18.2) async (~> 2.10) - async-http (0.66.3) + async-http (0.69.0) async (>= 2.10.2) - async-pool (>= 0.6.1) - io-endpoint (~> 0.10, >= 0.10.3) + async-pool (~> 0.7) + io-endpoint (~> 0.11) io-stream (~> 0.4) - protocol-http (~> 0.26.0) - protocol-http1 (~> 0.19.0) - protocol-http2 (~> 0.17.0) - traces (>= 0.10.0) + protocol-http (~> 0.26) + protocol-http1 (~> 0.19) + protocol-http2 (~> 0.18) + traces (>= 0.10) async-http-cache (0.4.3) async-http (~> 0.56) - async-pool (0.6.1) + async-pool (0.7.0) async (>= 1.25) async-service (0.12.0) async async-container (~> 0.16) bigdecimal (3.1.8) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.3) connection_pool (2.4.1) console (1.25.2) fiber-annotation fiber-local (~> 1.1) json - falcon (0.47.6) + falcon (0.47.7) async async-container (~> 0.18) async-http (~> 0.66, >= 0.66.3) @@ -47,9 +46,9 @@ GEM fiber-annotation (0.2.0) fiber-local (1.1.0) fiber-storage - fiber-storage (0.1.0) - io-endpoint (0.10.3) - io-event (1.5.1) + fiber-storage (0.1.2) + io-endpoint (0.11.0) + io-event (1.6.5) io-stream (0.4.0) json (2.7.2) kgio (2.11.4) @@ -57,11 +56,11 @@ GEM localhost (1.3.1) mapping (1.1.1) nio4r (2.7.3) - oj (3.16.3) + oj (3.16.4) bigdecimal (>= 3.0) openssl (3.2.0) - parallel (1.24.0) - parser (3.3.1.0) + parallel (1.25.1) + parser (3.3.3.0) ast (~> 2.4.1) racc pg (1.5.6) @@ -69,27 +68,26 @@ GEM console (~> 1.8) samovar (~> 2.1) protocol-hpack (1.4.3) - protocol-http (0.26.5) + protocol-http (0.26.6) protocol-http1 (0.19.1) protocol-http (~> 0.22) - protocol-http2 (0.17.0) + protocol-http2 (0.18.0) protocol-hpack (~> 1.4) protocol-http (~> 0.18) - protocol-rack (0.5.1) + protocol-rack (0.6.0) protocol-http (~> 0.23) rack (>= 1.0) - puma (6.4.2) + puma (6.4.3) nio4r (~> 2.0) - racc (1.7.3) - rack (3.0.11) + racc (1.8.0) + rack (3.1.6) rack-test (2.1.0) rack (>= 1.3) rainbow (3.1.1) raindrops (0.20.1) - regexp_parser (2.9.1) - rexml (3.2.8) - strscan (>= 3.0.9) - rubocop (1.63.5) + regexp_parser (2.9.2) + rexml (3.3.9) + rubocop (1.64.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -106,13 +104,11 @@ GEM samovar (2.3.0) console (~> 1.0) mapping (~> 1.0) - sequel (5.80.0) + sequel (5.82.0) bigdecimal sequel_pg (1.17.1) pg (>= 0.18.0, != 1.2.0) sequel (>= 4.38.0) - strscan (3.1.0) - timers (4.3.5) traces (0.11.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) @@ -145,4 +141,4 @@ DEPENDENCIES unicorn (~> 6.1) BUNDLED WITH - 2.4.10 + 2.5.11 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 2fcacec605b..050b6eac801 100644 --- a/frameworks/Ruby/rack/hello_world.rb +++ b/frameworks/Ruby/rack/hello_world.rb @@ -30,13 +30,13 @@ class HelloWorld SERVER_STRING = if defined?(PhusionPassenger) 'Passenger' elsif defined?(Puma) - Puma::Const::PUMA_SERVER_STRING + 'Puma' elsif defined?(Unicorn) 'Unicorn' elsif defined?(Falcon) 'Falcon' else - ' Ruby Rack' + 'Ruby Rack' end TEMPLATE_PREFIX = ' diff --git a/frameworks/Ruby/rack/rack-falcon.dockerfile b/frameworks/Ruby/rack/rack-falcon.dockerfile index ef5a63a10e7..f030cd54177 100644 --- a/frameworks/Ruby/rack/rack-falcon.dockerfile +++ b/frameworks/Ruby/rack/rack-falcon.dockerfile @@ -5,14 +5,14 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 WORKDIR /rack -COPY Gemfile ./ +COPY Gemfile ./ ENV BUNDLE_FORCE_RUBY_PLATFORM=true -RUN bundle config set without 'development test' +RUN bundle config set without 'development test puma unicorn' RUN bundle install --jobs=8 COPY . . diff --git a/frameworks/Ruby/rack/rack-jruby.dockerfile b/frameworks/Ruby/rack/rack-jruby.dockerfile index 7bf4b329af1..c280cca6ac8 100644 --- a/frameworks/Ruby/rack/rack-jruby.dockerfile +++ b/frameworks/Ruby/rack/rack-jruby.dockerfile @@ -6,11 +6,13 @@ WORKDIR /rack COPY Gemfile ./ -RUN bundle config set without 'development test' +RUN bundle config set without 'development test falcon unicorn' RUN bundle install --jobs=8 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/rack/rack-unicorn.dockerfile b/frameworks/Ruby/rack/rack-unicorn.dockerfile index bc4807e5427..74b3e82041c 100644 --- a/frameworks/Ruby/rack/rack-unicorn.dockerfile +++ b/frameworks/Ruby/rack/rack-unicorn.dockerfile @@ -5,20 +5,18 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 WORKDIR /rack COPY Gemfile ./ ENV BUNDLE_FORCE_RUBY_PLATFORM=true -RUN bundle config set without 'development test' +RUN bundle config set without 'development test falcon puma' RUN bundle install --jobs=8 COPY . . EXPOSE 8080 -#CMD nginx -c /rack/config/nginx.conf && bundle exec unicorn -E production -c config/unicorn.rb - CMD bundle exec unicorn -c config/unicorn.rb -o 0.0.0.0 -p 8080 -E production diff --git a/frameworks/Ruby/rack/rack.dockerfile b/frameworks/Ruby/rack/rack.dockerfile index 5b51b2ef1e0..615775cf9ea 100644 --- a/frameworks/Ruby/rack/rack.dockerfile +++ b/frameworks/Ruby/rack/rack.dockerfile @@ -1,13 +1,19 @@ FROM ruby:3.4-rc -ENV BUNDLE_FORCE_RUBY_PLATFORM=true ENV RUBY_YJIT_ENABLE=1 +ENV RUBY_MN_THREADS=1 + +# Use Jemalloc +RUN apt-get update && \ + apt-get install -y --no-install-recommends libjemalloc2 +ENV LD_PRELOAD=libjemalloc.so.2 WORKDIR /rack -COPY Gemfile ./ +COPY Gemfile ./ -RUN bundle config set without 'development test' +ENV BUNDLE_FORCE_RUBY_PLATFORM=true +RUN bundle config set without 'development test falcon unicorn' RUN bundle install --jobs=8 COPY . . @@ -15,4 +21,3 @@ COPY . . EXPOSE 8080 CMD bundle exec puma -C config/puma.rb -b tcp://0.0.0.0:8080 -e production - diff --git a/frameworks/Ruby/rage/Gemfile b/frameworks/Ruby/rage/Gemfile index a7f090d01f9..89c3affcb58 100644 --- a/frameworks/Ruby/rage/Gemfile +++ b/frameworks/Ruby/rage/Gemfile @@ -1,9 +1,9 @@ source "https://rubygems.org" -gem "rage-rb", "~> 1.3" +gem "rage-rb", "~> 1.10" gem "pg", "~> 1.0" -gem "activerecord", "~> 7.0.0", require: "active_record" +gem "activerecord", "~> 7.2.0", require: "active_record" # Build JSON APIs with ease # gem "alba" diff --git a/frameworks/Ruby/rage/app/models/fortune.rb b/frameworks/Ruby/rage/app/models/fortune.rb index 6b7ad122f80..0080d6363c4 100644 --- a/frameworks/Ruby/rage/app/models/fortune.rb +++ b/frameworks/Ruby/rage/app/models/fortune.rb @@ -1,7 +1,3 @@ class Fortune < ApplicationRecord self.table_name = "Fortune" - - def as_json(*) - attributes - end end diff --git a/frameworks/Ruby/rage/app/models/world.rb b/frameworks/Ruby/rage/app/models/world.rb index 951aab55b64..836783137c6 100644 --- a/frameworks/Ruby/rage/app/models/world.rb +++ b/frameworks/Ruby/rage/app/models/world.rb @@ -1,9 +1,5 @@ class World < ApplicationRecord self.table_name = "World" - def as_json(*) - attributes - end - alias_attribute(:randomNumber, :randomnumber) end diff --git a/frameworks/Ruby/rage/config.ru b/frameworks/Ruby/rage/config.ru index 52de8a40479..049a1ad509d 100644 --- a/frameworks/Ruby/rage/config.ru +++ b/frameworks/Ruby/rage/config.ru @@ -1,4 +1,3 @@ require_relative "config/application" run Rage.application -Rage.load_middlewares(self) diff --git a/frameworks/Ruby/rage/config/initializers/activerecord.rb b/frameworks/Ruby/rage/config/initializers/activerecord.rb index c0e3eb08d44..34a3b019a66 100644 --- a/frameworks/Ruby/rage/config/initializers/activerecord.rb +++ b/frameworks/Ruby/rage/config/initializers/activerecord.rb @@ -2,16 +2,7 @@ require "etc" -connection = { - adapter: "postgresql", - host: "tfb-database", - username: "benchmarkdbuser", - password: "benchmarkdbpass", - database: "hello_world", - reaping_frequency: 0, - pool: (2 * Math.log(256 / Etc.nprocessors)).floor -} +pool_size = (2 * Math.log(256 / Etc.nprocessors)).floor +puts "ActiveRecord pool size: #{pool_size}" -puts "ActiveRecord connection options: #{connection.inspect}" - -ActiveRecord::Base.establish_connection(connection) +ENV["DATABASE_URL"]="postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world?pool=#{pool_size}&reaping_frequency=0" diff --git a/frameworks/Ruby/rage/rage.dockerfile b/frameworks/Ruby/rage/rage.dockerfile index a1e80a095f3..6c65b51fba4 100644 --- a/frameworks/Ruby/rage/rage.dockerfile +++ b/frameworks/Ruby/rage/rage.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc EXPOSE 8080 WORKDIR /rage @@ -8,7 +8,6 @@ RUN bundle install --jobs=8 COPY . /rage ENV RUBY_YJIT_ENABLE=1 -ENV RAGE_PATCH_AR_POOL=1 ENV BUNDLE_FORCE_RUBY_PLATFORM=true CMD bundle exec rage s -b 0.0.0.0 -p 8080 -e production diff --git a/frameworks/Ruby/rails/.ruby-version b/frameworks/Ruby/rails/.ruby-version index fd2a01863fd..15a27998172 100644 --- a/frameworks/Ruby/rails/.ruby-version +++ b/frameworks/Ruby/rails/.ruby-version @@ -1 +1 @@ -3.1.0 +3.3.0 diff --git a/frameworks/Ruby/rails/Gemfile b/frameworks/Ruby/rails/Gemfile index 39eb973e700..b188913327b 100644 --- a/frameworks/Ruby/rails/Gemfile +++ b/frameworks/Ruby/rails/Gemfile @@ -1,9 +1,31 @@ source 'https://rubygems.org' gem 'oj', '~> 3.16' -gem 'pg', '~> 1.5', group: :postgresql -gem 'puma', '~> 6.4' -gem 'rails', '~> 7.1.3' +gem 'rails', '~> 7.2.0' gem 'redis', '~> 5.0' -gem 'trilogy', '~> 2.8.1', group: :mysql gem 'tzinfo-data' + +group :mysql do + gem 'trilogy', '~> 2.8.1' +end + +group :postgresql do + gem 'pg', '~> 1.5' +end + +group :falcon do + gem 'falcon', '~> 0.47', require: false +end + +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' +end diff --git a/frameworks/Ruby/rails/Gemfile.lock b/frameworks/Ruby/rails/Gemfile.lock index 5c25771e659..9773df3e123 100644 --- a/frameworks/Ruby/rails/Gemfile.lock +++ b/frameworks/Ruby/rails/Gemfile.lock @@ -1,97 +1,146 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) + actioncable (7.2.1.1) + actionpack (= 7.2.1.1) + activesupport (= 7.2.1.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.3.4) - actionpack (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + 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) + 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) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.3.4) - actionview (= 7.1.3.4) - activesupport (= 7.1.3.4) + actionpack (7.2.1.1) + actionview (= 7.2.1.1) + activesupport (= 7.2.1.1) nokogiri (>= 1.8.5) racc - rack (>= 2.2.4) + rack (>= 2.2.4, < 3.2) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3.4) - actionpack (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) + 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) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3.4) - activesupport (= 7.1.3.4) + actionview (7.2.1.1) + activesupport (= 7.2.1.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.3.4) - activesupport (= 7.1.3.4) + activejob (7.2.1.1) + activesupport (= 7.2.1.1) globalid (>= 0.3.6) - activemodel (7.1.3.4) - activesupport (= 7.1.3.4) - activerecord (7.1.3.4) - activemodel (= 7.1.3.4) - activesupport (= 7.1.3.4) + 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) - activestorage (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activesupport (= 7.1.3.4) + 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) marcel (~> 1.0) - activesupport (7.1.3.4) + activesupport (7.2.1.1) base64 bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + agoo (2.15.13) + async (2.17.0) + console (~> 1.26) + fiber-annotation + io-event (~> 1.6, >= 1.6.5) + async-container (0.18.3) + async (~> 2.10) + async-http (0.82.1) + async (>= 2.10.2) + async-pool (~> 0.9) + io-endpoint (~> 0.14) + io-stream (~> 0.6) + metrics (~> 0.12) + protocol-http (~> 0.37) + 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.10.1) + async (>= 1.25) + traces + async-service (0.12.0) + async + async-container (~> 0.16) base64 (0.2.0) bigdecimal (3.1.8) - builder (3.2.4) - concurrent-ruby (1.3.1) + builder (3.3.0) + concurrent-ruby (1.3.4) connection_pool (2.4.1) + console (1.27.0) + fiber-annotation + fiber-local (~> 1.1) + json crass (1.0.6) date (3.3.4) drb (2.2.1) - erubi (1.12.0) + erubi (1.13.0) + falcon (0.48.3) + async + async-container (~> 0.18) + async-http (~> 0.75) + async-http-cache (~> 0.4) + async-service (~> 0.10) + bundler + localhost (~> 1.1) + openssl (~> 3.0) + 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 (1.0.0) globalid (1.2.1) activesupport (>= 6.1) - i18n (1.14.5) + i18n (1.14.6) concurrent-ruby (~> 1.0) io-console (0.7.2) - irb (1.13.1) + io-endpoint (0.14.0) + io-event (1.7.2) + io-stream (0.6.0) + irb (1.14.1) rdoc (>= 4.0.0) reline (>= 0.4.2) + json (2.7.2) + kgio (2.11.4) + localhost (1.3.1) + logger (1.6.1) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -100,12 +149,13 @@ GEM net-imap net-pop net-smtp + mapping (1.1.1) marcel (1.0.4) + metrics (0.12.0) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.23.1) - mutex_m (0.2.0) - net-imap (0.4.11) + minitest (5.25.1) + net-imap (0.4.17) date net-protocol net-pop (0.1.2) @@ -115,22 +165,39 @@ GEM net-smtp (0.5.0) net-protocol nio4r (2.7.3) - nokogiri (1.16.5) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.5-arm64-darwin) + nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.5-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) - oj (3.16.3) + oj (3.16.6) bigdecimal (>= 3.0) - pg (1.5.6) + ostruct (>= 0.2) + openssl (3.2.0) + ostruct (0.6.0) + pg (1.5.8) + process-metrics (0.3.0) + console (~> 1.8) + json (~> 2) + samovar (~> 2.1) + protocol-hpack (1.5.1) + protocol-http (0.40.0) + protocol-http1 (0.28.1) + protocol-http (~> 0.22) + protocol-http2 (0.19.3) + protocol-hpack (~> 1.4) + protocol-http (~> 0.18) + protocol-rack (0.10.1) + protocol-http (~> 0.37) + rack (>= 1.0) psych (5.1.2) stringio - puma (6.4.2) + puma (6.4.3) nio4r (~> 2.0) - racc (1.8.0) - rack (3.0.11) + racc (1.8.1) + rack (3.1.8) rack-session (2.0.0) rack (>= 3.0.0) rack-test (2.1.0) @@ -138,20 +205,20 @@ GEM rackup (2.1.0) rack (>= 3) webrick (~> 1.8) - rails (7.1.3.4) - actioncable (= 7.1.3.4) - actionmailbox (= 7.1.3.4) - actionmailer (= 7.1.3.4) - actionpack (= 7.1.3.4) - actiontext (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activemodel (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) + 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) bundler (>= 1.15.0) - railties (= 7.1.3.4) + railties (= 7.2.1.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -159,36 +226,46 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) - irb + railties (7.2.1.1) + actionpack (= 7.2.1.1) + activesupport (= 7.2.1.1) + 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.6.3.1) + rdoc (6.7.0) psych (>= 4.0.0) - redis (5.2.0) + redis (5.3.0) redis-client (>= 0.22.0) redis-client (0.22.2) connection_pool - reline (0.5.7) + reline (0.5.10) io-console (~> 0.5) - stringio (3.1.0) - thor (1.3.1) + samovar (2.3.0) + console (~> 1.0) + mapping (~> 1.0) + securerandom (0.3.1) + stringio (3.1.1) + thor (1.3.2) timeout (0.4.1) + traces (0.13.1) trilogy (2.8.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - tzinfo-data (1.2024.1) + tzinfo-data (1.2024.2) tzinfo (>= 1.0.0) - webrick (1.8.1) + unicorn (6.1.0) + kgio (~> 2.6) + raindrops (~> 0.7) + useragent (0.16.10) + webrick (1.8.2) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.6.14) + zeitwerk (2.7.0) PLATFORMS arm64-darwin-20 @@ -196,13 +273,17 @@ PLATFORMS x86_64-linux DEPENDENCIES + agoo + falcon (~> 0.47) oj (~> 3.16) pg (~> 1.5) puma (~> 6.4) - rails (~> 7.1.3) + rackup + rails (~> 7.2.0) redis (~> 5.0) trilogy (~> 2.8.1) tzinfo-data + unicorn (~> 6.1) BUNDLED WITH 2.3.3 diff --git a/frameworks/Ruby/rails/app/models/world.rb b/frameworks/Ruby/rails/app/models/world.rb index 278501c05f4..bc1c8e9311b 100644 --- a/frameworks/Ruby/rails/app/models/world.rb +++ b/frameworks/Ruby/rails/app/models/world.rb @@ -3,4 +3,14 @@ class World < ApplicationRecord alias_attribute(:randomNumber, :randomnumber) \ if connection.adapter_name.downcase.start_with?('postgres') + + if connection.adapter_name.downcase.start_with?('trilogy') + def self.upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil) + # On MySQL Batch updates verification isn't supported yet by TechEmpower. + # https://github.com/TechEmpower/FrameworkBenchmarks/issues/5983 + attributes.each do |attrs| + where(id: attrs[:id]).update_all(randomNumber: attrs[:randomNumber]) + end + end + end end diff --git a/frameworks/Ruby/rails/benchmark_config.json b/frameworks/Ruby/rails/benchmark_config.json index 7e309e25450..f663c793a3d 100644 --- a/frameworks/Ruby/rails/benchmark_config.json +++ b/frameworks/Ruby/rails/benchmark_config.json @@ -44,6 +44,52 @@ "display_name": "rails-mysql", "notes": "", "versus": "rack-puma-mri" + }, + "falcon": { + "db_url": "/db", + "json_url": "/json", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "update_url": "/updates?queries=", + "plaintext_url": "/plaintext", + "cached_query_url": "/cached?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "Postgres", + "framework": "rails", + "language": "Ruby", + "orm": "Full", + "platform": "Rack", + "webserver": "Falcon", + "os": "Linux", + "database_os": "Linux", + "display_name": "rails-falcon", + "notes": "", + "versus": "rack-falcon-mri" + }, + "agoo": { + "db_url": "/db", + "json_url": "/json", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "update_url": "/updates?queries=", + "plaintext_url": "/plaintext", + "cached_query_url": "/cached?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "Postgres", + "framework": "rails", + "language": "Ruby", + "orm": "Full", + "platform": "Rack", + "webserver": "Agoo", + "os": "Linux", + "database_os": "Linux", + "display_name": "rails-agoo", + "notes": "", + "versus": "" } }] } diff --git a/frameworks/Ruby/rails/config/agoo.rb b/frameworks/Ruby/rails/config/agoo.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/frameworks/Ruby/rails/config/application.rb b/frameworks/Ruby/rails/config/application.rb index 2cca7e95baa..291c4f983ae 100644 --- a/frameworks/Ruby/rails/config/application.rb +++ b/frameworks/Ruby/rails/config/application.rb @@ -8,9 +8,10 @@ # require "active_storage/engine" require "action_controller/railtie" # require "action_mailer/railtie" +# require "action_mailbox/engine" +# require "action_text/engine" require "action_view/railtie" # require "action_cable/engine" -# require "sprockets/railtie" # require "rails/test_unit/railtie" # Require the gems listed in Gemfile, including any gems @@ -20,36 +21,37 @@ module Hello class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 7.1 + config.load_defaults 7.2 - # Settings in config/environments/* take precedence over those specified here. - # Application configuration can go into files in config/initializers - # -- all .rb files in that directory are automatically loaded after loading - # the framework and any gems in your application. + # Please, add to the `ignore` list any other `lib` subdirectories that do + # not contain `.rb` files, or that should not be reloaded or eager loaded. + # Common ones are `templates`, `generators`, or `middleware`, for example. + config.autoload_lib(ignore: %w[assets tasks]) config.action_dispatch.default_headers.merge!('Server' => 'WebServer') - config.middleware.delete ActionDispatch::HostAuthorization - config.middleware.delete Rack::Sendfile - config.middleware.delete ActionDispatch::Static + config.middleware.delete ActionDispatch::Callbacks + config.middleware.delete ActionDispatch::ContentSecurityPolicy::Middleware + config.middleware.delete ActionDispatch::Cookies + config.middleware.delete ActionDispatch::DebugExceptions config.middleware.delete ActionDispatch::Executor - config.middleware.delete Rack::Runtime - config.middleware.delete Rack::MethodOverride - config.middleware.delete ActionDispatch::RequestId + config.middleware.delete ActionDispatch::Flash + config.middleware.delete ActionDispatch::PermissionsPolicy::Middleware + config.middleware.delete ActionDispatch::Reloader config.middleware.delete ActionDispatch::RemoteIp - config.middleware.delete Rails::Rack::Logger + config.middleware.delete ActionDispatch::RequestId + config.middleware.delete ActionDispatch::Session::CookieStore config.middleware.delete ActionDispatch::ShowExceptions - config.middleware.delete ActionDispatch::DebugExceptions - config.middleware.delete ActionDispatch::ActionableExceptions - config.middleware.delete ActionDispatch::Reloader config.middleware.delete ActiveRecord::Migration::CheckPending - config.middleware.delete ActionDispatch::Cookies - config.middleware.delete ActionDispatch::Session::CookieStore - config.middleware.delete ActionDispatch::Flash - config.middleware.delete ActionDispatch::ContentSecurityPolicy::Middleware - config.middleware.delete ActionDispatch::PermissionsPolicy::Middleware - config.middleware.delete Rack::Head config.middleware.delete Rack::ConditionalGet config.middleware.delete Rack::ETag + config.middleware.delete Rack::Head + config.middleware.delete Rack::MethodOverride + config.middleware.delete Rack::Runtime + config.middleware.delete Rack::Sendfile + config.middleware.delete Rack::TempfileReaper + config.middleware.delete Rails::Rack::Logger + + config.active_support.isolation_level = :fiber if defined?(Falcon) end end diff --git a/frameworks/Ruby/rails/config/environments/development.rb b/frameworks/Ruby/rails/config/environments/development.rb index 8620b53db5a..6a2306dbabc 100644 --- a/frameworks/Ruby/rails/config/environments/development.rb +++ b/frameworks/Ruby/rails/config/environments/development.rb @@ -6,7 +6,7 @@ # 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. - config.cache_classes = false + config.enable_reloading = true # Do not eager load code on boot. config.eager_load = false @@ -14,7 +14,7 @@ # Show full error reports. config.consider_all_requests_local = true - # Enable server timing + # Enable server timing. config.server_timing = true # Enable/disable caching. By default caching is disabled. @@ -24,9 +24,7 @@ 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 @@ -48,16 +46,15 @@ # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true # Annotate rendered view with file names. - # config.action_view.annotate_rendered_view_with_filenames = true - - # Uncomment if you wish to allow Action Cable access from any origin. - # config.action_cable.disable_request_forgery_protection = true + config.action_view.annotate_rendered_view_with_filenames = true - # Raise error when a before_action's only/except options reference missing actions + # 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 3ad22143e26..c6594e5d17d 100644 --- a/frameworks/Ruby/rails/config/environments/production.rb +++ b/frameworks/Ruby/rails/config/environments/production.rb @@ -4,7 +4,7 @@ # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. - config.cache_classes = true + 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 @@ -13,16 +13,16 @@ config.eager_load = true # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false + config.consider_all_requests_local = false config.action_controller.perform_caching = true - # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] - # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # 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 - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? + # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. + # This disables the ActionDispatch::Static middleware. + config.public_file_server.enabled = false # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" @@ -31,16 +31,24 @@ # 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. # config.force_ssl = true - # Include generic and useful information about system operation, but avoid logging too much - # information to avoid inadvertent exposure of personally identifiable information (PII). - config.log_level = :info + # 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 ] + # "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". + config.log_level = :fatal + # Use a different cache store in production. config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'], @@ -54,10 +62,6 @@ } } - # Use a real queuing backend for Active Job (and separate queues per environment). - # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "hello_production" - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true @@ -65,19 +69,14 @@ # Don't log any deprecations. config.active_support.report_deprecations = false - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new - - # Use a different logger for distributed setups. - # require "syslog/logger" - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") - - if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) - logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) - end - # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false + + # 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 bfcd30081b1..999db7091ef 100644 --- a/frameworks/Ruby/rails/config/environments/test.rb +++ b/frameworks/Ruby/rails/config/environments/test.rb @@ -8,27 +8,25 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # Turn false under Spring and add config.action_view.cache_template_loading = true. - config.cache_classes = true + # While tests run files are not watched, reloading is not necessary. + config.enable_reloading = false - # Eager loading loads your whole application. When running a single test locally, - # this probably isn't necessary. It's a good idea to do in a continuous integration - # system, or in some way before deploying your code. + # Eager loading loads your entire application. When running a single test locally, + # this is usually not necessary, and can slow down your test suite. However, it's + # recommended that you enable it in continuous integration systems to ensure eager + # 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.enabled = true - config.public_file_server.headers = { - "Cache-Control" => "public, max-age=#{1.hour.to_i}" - } + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. - config.consider_all_requests_local = true + config.consider_all_requests_local = true config.action_controller.perform_caching = false config.cache_store = :null_store - # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false + # Render exception templates for rescuable exceptions and raise for other exceptions. + config.action_dispatch.show_exceptions = :rescuable # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false @@ -48,6 +46,6 @@ # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true end diff --git a/frameworks/Ruby/rails/config/falcon_preload.rb b/frameworks/Ruby/rails/config/falcon_preload.rb new file mode 100644 index 00000000000..647c7b948d9 --- /dev/null +++ b/frameworks/Ruby/rails/config/falcon_preload.rb @@ -0,0 +1,3 @@ +# required by Falcon: +# https://github.com/socketry/falcon/blob/19fe8ece7cc49aa03222afe2c940682aeb69fe37/guides/rails-integration/readme.md?plain=1#L38 +require_relative "../config/environment" diff --git a/frameworks/Ruby/rails/config/puma.rb b/frameworks/Ruby/rails/config/puma.rb index 3089f8334a2..b513db258e6 100644 --- a/frameworks/Ruby/rails/config/puma.rb +++ b/frameworks/Ruby/rails/config/puma.rb @@ -1,49 +1,40 @@ -require_relative 'auto_tune' +# This configuration file will be evaluated by Puma. The top-level methods that +# are invoked here are part of Puma's configuration DSL. For more information +# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. -# FWBM only... use the puma_auto_tune gem in production! -tuned_num_workers, tuned_num_threads = auto_tune +require_relative 'auto_tune' -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. # -max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 3 } -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } -threads min_threads_count, max_threads_count - -# Specifies the `worker_timeout` threshold that Puma will use to wait before -# terminating a worker in development environments. +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# to prioritize throughput over latency. # -worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" - -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. # -port ENV.fetch("PORT") { 3000 } - -# Specifies the `environment` that Puma will run in. +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. # -environment ENV.fetch("RAILS_ENV") { "development" } +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count -# Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked web server processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). -# -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } -workers tuned_num_workers +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +port ENV.fetch("PORT", 3000) -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. -# -preload_app! +tuned_num_workers, tuned_num_threads = auto_tune +workers tuned_num_workers -# Allow puma to be restarted by `rails restart` command. +# Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart + +# Only use a pidfile when requested +pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/frameworks/Ruby/rails/falcon.rb b/frameworks/Ruby/rails/falcon.rb new file mode 100644 index 00000000000..147d6b3b66f --- /dev/null +++ b/frameworks/Ruby/rails/falcon.rb @@ -0,0 +1,12 @@ +#!/usr/bin/env -S falcon host +# frozen_string_literal: true + +load :rack + +hostname = File.basename(__dir__) +port = ENV["PORT"] || 8080 + +rack hostname do + append preload "config/falcon_preload.rb" + endpoint Async::HTTP::Endpoint.parse("http://0.0.0.0:#{port}") +end diff --git a/frameworks/Ruby/rails/rails-agoo.dockerfile b/frameworks/Ruby/rails/rails-agoo.dockerfile new file mode 100644 index 00000000000..7160fe32f84 --- /dev/null +++ b/frameworks/Ruby/rails/rails-agoo.dockerfile @@ -0,0 +1,27 @@ +FROM ruby:3.4-rc + +RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends redis-server + +EXPOSE 8080 +WORKDIR /rails + +# ENV RUBY_YJIT_ENABLE=1 YJIT is enabled in config/initializers/enable_yjit.rb + +# Use Jemalloc +RUN apt-get update && \ + apt-get install -y --no-install-recommends libjemalloc2 +ENV LD_PRELOAD=libjemalloc.so.2 + +COPY ./Gemfile* /rails/ + +ENV BUNDLE_FORCE_RUBY_PLATFORM=true +ENV BUNDLE_WITHOUT=trilogy +RUN bundle install --jobs=8 + +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) diff --git a/frameworks/Ruby/rails/rails-falcon.dockerfile b/frameworks/Ruby/rails/rails-falcon.dockerfile new file mode 100644 index 00000000000..3244b73aa02 --- /dev/null +++ b/frameworks/Ruby/rails/rails-falcon.dockerfile @@ -0,0 +1,26 @@ +FROM ruby:3.4-rc + +RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends redis-server + +EXPOSE 8080 +WORKDIR /rails + +# ENV RUBY_YJIT_ENABLE=1 YJIT is enabled in config/initializers/enable_yjit.rb + +# Use Jemalloc +RUN apt-get update && \ + apt-get install -y --no-install-recommends libjemalloc2 +ENV LD_PRELOAD=libjemalloc.so.2 + +COPY ./Gemfile* /rails/ + +ENV BUNDLE_FORCE_RUBY_PLATFORM=true +ENV BUNDLE_WITHOUT=mysql +RUN bundle install --jobs=8 + +COPY . /rails/ + +ENV RAILS_ENV=production_postgresql +ENV PORT=8080 +ENV REDIS_URL=redis://localhost:6379/0 +CMD bundle exec falcon host diff --git a/frameworks/Ruby/rails/rails-mysql.dockerfile b/frameworks/Ruby/rails/rails-mysql.dockerfile index 370b08ba493..b14a3b75af6 100644 --- a/frameworks/Ruby/rails/rails-mysql.dockerfile +++ b/frameworks/Ruby/rails/rails-mysql.dockerfile @@ -1,16 +1,16 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends redis-server EXPOSE 8080 WORKDIR /rails -ENV RUBY_YJIT_ENABLE=1 +# ENV RUBY_YJIT_ENABLE=1 YJIT is enabled in config/initializers/enable_yjit.rb # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 COPY ./Gemfile* /rails/ diff --git a/frameworks/Ruby/rails/rails.dockerfile b/frameworks/Ruby/rails/rails.dockerfile index 3e02770b190..8eb53c4dbd2 100644 --- a/frameworks/Ruby/rails/rails.dockerfile +++ b/frameworks/Ruby/rails/rails.dockerfile @@ -1,20 +1,21 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends redis-server EXPOSE 8080 WORKDIR /rails -ENV RUBY_YJIT_ENABLE=1 +# ENV RUBY_YJIT_ENABLE=1 YJIT is enabled in config/initializers/enable_yjit.rb + # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 COPY ./Gemfile* /rails/ ENV BUNDLE_FORCE_RUBY_PLATFORM=true -ENV BUNDLE_WITHOUT=trilogy +ENV BUNDLE_WITHOUT=mysql RUN bundle install --jobs=8 COPY . /rails/ diff --git a/frameworks/Ruby/rails/run-with-redis.sh b/frameworks/Ruby/rails/run-with-redis.sh index 9ce9b243b74..036224f0df5 100755 --- a/frameworks/Ruby/rails/run-with-redis.sh +++ b/frameworks/Ruby/rails/run-with-redis.sh @@ -1,3 +1,3 @@ #!/bin/bash service redis-server start -rails server +bundle exec falcon host diff --git a/frameworks/Ruby/roda-sequel/Gemfile b/frameworks/Ruby/roda-sequel/Gemfile index c9c6a48ab89..c5dd09c3375 100644 --- a/frameworks/Ruby/roda-sequel/Gemfile +++ b/frameworks/Ruby/roda-sequel/Gemfile @@ -1,5 +1,6 @@ source "https://rubygems.org" +gem 'base64' # required by passenger on Ruby 3.4 gem "erubi", "~> 1.12" gem "passenger", "~> 6.0", platforms: %i[ruby mswin], require: false gem "puma", "~> 6.2", require: false @@ -7,7 +8,7 @@ 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 "oj", "~> 3.14" +gem "rapidjson" group :mysql do gem "mysql2", "~> 0.5", platforms: %i[ruby mswin] 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/benchmark_config.json b/frameworks/Ruby/roda-sequel/benchmark_config.json index 27db92a9b59..2ca8400a156 100644 --- a/frameworks/Ruby/roda-sequel/benchmark_config.json +++ b/frameworks/Ruby/roda-sequel/benchmark_config.json @@ -44,28 +44,6 @@ "versus": "rack-sequel-postgres-puma-mri", "notes": "" }, - "passenger-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": "MySQL", - "framework": "roda-sequel", - "language": "Ruby", - "orm": "Full", - "platform": "Rack", - "webserver": "Passenger", - "os": "Linux", - "database_os": "Linux", - "display_name": "roda-sequel-passenger-mri", - "versus": "rack-sequel-passenger-mri", - "notes": "" - }, "postgres-passenger-mri": { "db_url": "/db", "query_url": "/queries?queries=", @@ -86,28 +64,6 @@ "versus": "rack-sequel-postgres-passenger-mri", "notes": "" }, - "unicorn-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": "MySQL", - "framework": "roda-sequel", - "language": "Ruby", - "orm": "Full", - "platform": "Rack", - "webserver": "Unicorn", - "os": "Linux", - "database_os": "Linux", - "display_name": "roda-sequel-unicorn-mri", - "versus": "rack-sequel-unicorn-mri", - "notes": "" - }, "postgres-unicorn-mri": { "db_url": "/db", "query_url": "/queries?queries=", diff --git a/frameworks/Ruby/roda-sequel/boot.rb b/frameworks/Ruby/roda-sequel/boot.rb index ebeb5a584c4..bc7277e2362 100644 --- a/frameworks/Ruby/roda-sequel/boot.rb +++ b/frameworks/Ruby/roda-sequel/boot.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "bundler/setup" require "time" -require "oj" +require "rapidjson" MAX_PK = 10_000 QUERY_RANGE = (1..MAX_PK).freeze ALL_IDS = QUERY_RANGE.to_a @@ -9,9 +9,6 @@ QUERIES_MAX = 500 SEQUEL_NO_ASSOCIATIONS = true -# Use the OJ gem instead of the JSON one -Oj.mimic_JSON() - SERVER_STRING = if defined?(PhusionPassenger) [ diff --git a/frameworks/Ruby/roda-sequel/hello_world.rb b/frameworks/Ruby/roda-sequel/hello_world.rb index 6a0a43efccb..458e1a7c687 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 - { message: "Hello, World!" }.to_json + RapidJSON.encode({ message: "Hello, World!" }) end # Test type 2: Single database query r.is "db" do response[CONTENT_TYPE] = JSON_TYPE - World.with_pk(rand1).values.to_json + RapidJSON.encode(World.with_pk(rand1).values) end # Test type 3: Multiple database queries @@ -40,7 +40,7 @@ def rand1 World.with_pk(id).values end end - worlds.to_json + RapidJSON.encode(worlds) end # Test type 4: Fortunes @@ -70,7 +70,7 @@ def rand1 end World.batch_update(worlds) end - worlds.map(&:values).to_json + RapidJSON.encode(worlds.map!(&:values)) end # Test type 6: Plaintext diff --git a/frameworks/Ruby/roda-sequel/roda-sequel-passenger-mri.dockerfile b/frameworks/Ruby/roda-sequel/roda-sequel-passenger-mri.dockerfile deleted file mode 100644 index 0af00a72d36..00000000000 --- a/frameworks/Ruby/roda-sequel/roda-sequel-passenger-mri.dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM ruby:3.3 - -ADD ./ /roda-sequel -WORKDIR /roda-sequel - -ENV RUBY_YJIT_ENABLE=1 - -# Use Jemalloc -RUN apt-get update && \ - apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 - -ENV BUNDLE_FORCE_RUBY_PLATFORM=true -RUN bundle install --jobs=8 - -# TODO: https://github.com/phusion/passenger/issues/1916 -ENV _PASSENGER_FORCE_HTTP_SESSION=true -ENV DBTYPE=mysql - -RUN ruby -r /roda-sequel/config/auto_tune -e 'puts auto_tune.first' > instances - -EXPOSE 8080 - -CMD bundle exec passenger start --log-level 1 \ - --engine builtin --disable-turbocaching --disable-security-update-check \ - --spawn-method direct --max-pool-size $(cat instances) --min-instances $(cat instances) --max-request-queue-size 1024 \ - --address 0.0.0.0 --port 8080 --environment production 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 a24b9881d00..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 @@ -8,7 +8,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ENV BUNDLE_FORCE_RUBY_PLATFORM=true RUN bundle install --jobs=8 diff --git a/frameworks/Ruby/roda-sequel/roda-sequel-postgres-unicorn-mri.dockerfile b/frameworks/Ruby/roda-sequel/roda-sequel-postgres-unicorn-mri.dockerfile index 3cde40eaa97..4865d5feebd 100644 --- a/frameworks/Ruby/roda-sequel/roda-sequel-postgres-unicorn-mri.dockerfile +++ b/frameworks/Ruby/roda-sequel/roda-sequel-postgres-unicorn-mri.dockerfile @@ -8,7 +8,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ENV BUNDLE_FORCE_RUBY_PLATFORM=true RUN bundle install --jobs=8 diff --git a/frameworks/Ruby/roda-sequel/roda-sequel-postgres.dockerfile b/frameworks/Ruby/roda-sequel/roda-sequel-postgres.dockerfile index c74c9b9f0d3..d71a4b28a78 100644 --- a/frameworks/Ruby/roda-sequel/roda-sequel-postgres.dockerfile +++ b/frameworks/Ruby/roda-sequel/roda-sequel-postgres.dockerfile @@ -8,7 +8,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ENV BUNDLE_FORCE_RUBY_PLATFORM=true RUN bundle install --jobs=8 diff --git a/frameworks/Ruby/roda-sequel/roda-sequel-unicorn-mri.dockerfile b/frameworks/Ruby/roda-sequel/roda-sequel-unicorn-mri.dockerfile deleted file mode 100644 index cda64c5c29b..00000000000 --- a/frameworks/Ruby/roda-sequel/roda-sequel-unicorn-mri.dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM ruby:3.4-rc - -ADD ./ /roda-sequel -WORKDIR /roda-sequel - -ENV RUBY_YJIT_ENABLE=1 - -# Use Jemalloc -RUN apt-get update && \ - apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 - -ENV BUNDLE_FORCE_RUBY_PLATFORM=true -RUN bundle install --jobs=8 - -ENV DBTYPE=mysql - -EXPOSE 8080 - -CMD bundle exec unicorn -c config/mri_unicorn.rb -o 0.0.0.0 -p 8080 -E production diff --git a/frameworks/Ruby/roda-sequel/roda-sequel.dockerfile b/frameworks/Ruby/roda-sequel/roda-sequel.dockerfile index efa9d35597e..382f31291a1 100644 --- a/frameworks/Ruby/roda-sequel/roda-sequel.dockerfile +++ b/frameworks/Ruby/roda-sequel/roda-sequel.dockerfile @@ -8,7 +8,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ENV BUNDLE_FORCE_RUBY_PLATFORM=true RUN bundle install --jobs=8 diff --git a/frameworks/Ruby/sinatra-sequel/Gemfile b/frameworks/Ruby/sinatra-sequel/Gemfile index 649fbcfe6bf..4aace641277 100644 --- a/frameworks/Ruby/sinatra-sequel/Gemfile +++ b/frameworks/Ruby/sinatra-sequel/Gemfile @@ -4,8 +4,7 @@ gem 'oj' gem 'passenger', '~> 6.0', :platforms=>[:ruby, :mswin], :require=>false gem 'puma', '~> 6.4', :require=>false gem 'sequel', '~> 5.0' -gem 'sinatra', '~> 3.0', :require=>'sinatra/base' -gem 'slim', '~> 3.0' +gem 'sinatra', '~> 4.0', :require=>'sinatra/base' gem 'unicorn', '~> 6.1', :platforms=>[:ruby, :mswin], :require=>false group :mysql do diff --git a/frameworks/Ruby/sinatra-sequel/README.md b/frameworks/Ruby/sinatra-sequel/README.md index 147e20cb2ed..9f382526d82 100644 --- a/frameworks/Ruby/sinatra-sequel/README.md +++ b/frameworks/Ruby/sinatra-sequel/README.md @@ -15,9 +15,9 @@ The tests will be run with: * [Ruby 3.3](http://www.ruby-lang.org) * [JRuby 9.4](http://jruby.org) * [Puma 6](http://puma.io) -* [Passenger 5](https://www.phusionpassenger.com) +* [Passenger 6](https://www.phusionpassenger.com) * [Unicorn 5](https://bogomips.org/unicorn/) -* [Sinatra 3](http://www.sinatrarb.com) +* [Sinatra 4](http://www.sinatrarb.com) * [Sequel 5](http://sequel.jeremyevans.net) * [Slim 3](http://slim-lang.com) * [MySQL 5.5](https://www.mysql.com) diff --git a/frameworks/Ruby/sinatra-sequel/benchmark_config.json b/frameworks/Ruby/sinatra-sequel/benchmark_config.json index c16da1e0e26..9a267813727 100644 --- a/frameworks/Ruby/sinatra-sequel/benchmark_config.json +++ b/frameworks/Ruby/sinatra-sequel/benchmark_config.json @@ -42,26 +42,6 @@ "versus": "rack-sequel-postgres-puma-mri", "notes": "" }, - "passenger-mri": { - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Micro", - "database": "MySQL", - "framework": "sinatra", - "language": "Ruby", - "orm": "Full", - "platform": "Rack", - "webserver": "Passenger", - "os": "Linux", - "database_os": "Linux", - "display_name": "sinatra-sequel-passenger-mri", - "versus": "rack-sequel-passenger-mri", - "notes": "" - }, "postgres-passenger-mri": { "db_url": "/db", "query_url": "/queries?queries=", @@ -82,26 +62,6 @@ "versus": "rack-sequel-postgres-passenger-mri", "notes": "" }, - "unicorn-mri": { - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Micro", - "database": "MySQL", - "framework": "sinatra", - "language": "Ruby", - "orm": "Full", - "platform": "Rack", - "webserver": "Unicorn", - "os": "Linux", - "database_os": "Linux", - "display_name": "sinatra-sequel-unicorn-mri", - "versus": "rack-sequel-unicorn-mri", - "notes": "" - }, "postgres-unicorn-mri": { "db_url": "/db", "query_url": "/queries?queries=", 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/hello_world.rb b/frameworks/Ruby/sinatra-sequel/hello_world.rb index e04d76a077b..822ceb5979c 100644 --- a/frameworks/Ruby/sinatra-sequel/hello_world.rb +++ b/frameworks/Ruby/sinatra-sequel/hello_world.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -# Configure Slim templating engine -Slim::Engine.set_options :format=>:html, :sort_attrs=>false - # Our Rack application to be executed by rackup class HelloWorld < Sinatra::Base configure do @@ -73,7 +70,7 @@ def rand1 ) @fortunes.sort_by!(&:message) - slim :fortunes + erb :fortunes, :layout=>true end # Test type 5: Database updates diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-base.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-base.dockerfile index 4fe264d4cb6..942d363b75c 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-base.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-base.dockerfile @@ -2,6 +2,11 @@ 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 diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-passenger-mri.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-passenger-mri.dockerfile deleted file mode 100644 index fe497718534..00000000000 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-passenger-mri.dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ruby:3.3 - -ENV RUBY_YJIT_ENABLE=1 - -ADD ./ /sinatra-sequel -WORKDIR /sinatra-sequel - -RUN bundle install --jobs=4 --gemfile=/sinatra-sequel/Gemfile - -# TODO: https://github.com/phusion/passenger/issues/1916 -ENV _PASSENGER_FORCE_HTTP_SESSION=true -ENV DBTYPE=mysql - -RUN ruby -r /sinatra-sequel/config/auto_tune -e 'puts auto_tune.first' > instances - -EXPOSE 8080 - -CMD bundle exec passenger start --log-level 1 \ - --engine builtin --disable-turbocaching --disable-security-update-check \ - --spawn-method direct --max-pool-size $(cat instances) --min-instances $(cat instances) --max-request-queue-size 1024 \ - --address 0.0.0.0 --port 8080 --environment production 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 74defc8326e..a7080367514 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-passenger-mri.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-passenger-mri.dockerfile @@ -1,7 +1,12 @@ -FROM ruby:3.3 +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 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 939eb97fce5..4d03257046a 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-unicorn-mri.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-unicorn-mri.dockerfile @@ -2,6 +2,11 @@ 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 diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile index ab54544763d..eee06752667 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile @@ -1,7 +1,12 @@ -FROM ruby:3.3 +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 diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-unicorn-mri.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-unicorn-mri.dockerfile deleted file mode 100644 index 78e45656c51..00000000000 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-unicorn-mri.dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM ruby:3.4-rc - -ENV RUBY_YJIT_ENABLE=1 - -ADD ./ /sinatra-sequel -WORKDIR /sinatra-sequel - -RUN bundle install --jobs=4 --gemfile=/sinatra-sequel/Gemfile - -ENV DBTYPE=mysql - -EXPOSE 8080 - -CMD bundle exec unicorn -c config/mri_unicorn.rb -o 0.0.0.0 -p 8080 -E production diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile index e36c111221c..80198c299ab 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile @@ -2,6 +2,11 @@ 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 diff --git a/frameworks/Ruby/sinatra-sequel/views/fortunes.erb b/frameworks/Ruby/sinatra-sequel/views/fortunes.erb new file mode 100644 index 00000000000..3d27f4dc50f --- /dev/null +++ b/frameworks/Ruby/sinatra-sequel/views/fortunes.erb @@ -0,0 +1,12 @@ + + + + + +<% @fortunes.each do |fortune| %> + + + + +<% end %> +
idmessage
<%= fortune.id %><%= Rack::Utils.escape_html(fortune.message) %>
diff --git a/frameworks/Ruby/sinatra-sequel/views/fortunes.slim b/frameworks/Ruby/sinatra-sequel/views/fortunes.slim deleted file mode 100644 index 14c18a58bc7..00000000000 --- a/frameworks/Ruby/sinatra-sequel/views/fortunes.slim +++ /dev/null @@ -1,8 +0,0 @@ -table - tr - th id - th message - - for fortune in @fortunes - tr - td =fortune.id - td =fortune.message \ No newline at end of file diff --git a/frameworks/Ruby/sinatra-sequel/views/layout.erb b/frameworks/Ruby/sinatra-sequel/views/layout.erb new file mode 100644 index 00000000000..7d6715a3e98 --- /dev/null +++ b/frameworks/Ruby/sinatra-sequel/views/layout.erb @@ -0,0 +1,11 @@ + + + + Fortunes + + + +<%= yield %> + + + diff --git a/frameworks/Ruby/sinatra-sequel/views/layout.slim b/frameworks/Ruby/sinatra-sequel/views/layout.slim deleted file mode 100644 index 2be47605c04..00000000000 --- a/frameworks/Ruby/sinatra-sequel/views/layout.slim +++ /dev/null @@ -1,6 +0,0 @@ -doctype html -html - head - title Fortunes - body - == yield \ No newline at end of file diff --git a/frameworks/Ruby/sinatra/Gemfile b/frameworks/Ruby/sinatra/Gemfile index 22752eec379..95fe5743580 100644 --- a/frameworks/Ruby/sinatra/Gemfile +++ b/frameworks/Ruby/sinatra/Gemfile @@ -1,16 +1,16 @@ source 'https://rubygems.org' -gem 'activerecord', '~> 7.0', :require=>'active_record' +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 'sinatra', '~> 3.0', :require=>'sinatra/base' -gem 'unicorn', '~> 6.1', :platforms=>[:ruby, :mswin], :require=>false +gem 'passenger', '~> 6.0', platforms: [:ruby, :mswin], require: false +gem 'puma', '~> 6.4', require: false +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] end group :postgresql do - gem 'pg', '~> 1.5', :platforms=>[:ruby, :mswin] + gem 'pg', '~> 1.5', platforms: [:ruby, :mswin] end diff --git a/frameworks/Ruby/sinatra/README.md b/frameworks/Ruby/sinatra/README.md index ff118f6ff21..e0f14b9a171 100644 --- a/frameworks/Ruby/sinatra/README.md +++ b/frameworks/Ruby/sinatra/README.md @@ -16,7 +16,7 @@ The tests will be run with: * [Puma 6](http://puma.io) * [Passenger 6](https://www.phusionpassenger.com) * [Unicorn 6](https://bogomips.org/unicorn/) -* [Sinatra 3](http://www.sinatrarb.com) +* [Sinatra 4](http://www.sinatrarb.com) * [ActiveRecord 7](https://github.com/rails/rails/tree/master/activerecord) * [MySQL 5.5](https://www.mysql.com) * [Postgres 9.3](https://www.postgresql.org) diff --git a/frameworks/Ruby/sinatra/benchmark_config.json b/frameworks/Ruby/sinatra/benchmark_config.json index 166a2e9e2bf..2c0be64ed47 100644 --- a/frameworks/Ruby/sinatra/benchmark_config.json +++ b/frameworks/Ruby/sinatra/benchmark_config.json @@ -44,28 +44,6 @@ "versus": "rack-postgres-puma-mri", "notes": "" }, - "passenger-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": "MySQL", - "framework": "sinatra", - "language": "Ruby", - "orm": "Full", - "platform": "Rack", - "webserver": "Passenger", - "os": "Linux", - "database_os": "Linux", - "display_name": "sinatra-passenger-mri", - "versus": "rack-passenger-mri", - "notes": "" - }, "postgres-passenger-mri": { "db_url": "/db", "query_url": "/queries?queries=", @@ -86,28 +64,6 @@ "versus": "rack-postgres-passenger-mri", "notes": "" }, - "unicorn-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": "MySQL", - "framework": "sinatra", - "language": "Ruby", - "orm": "Full", - "platform": "Rack", - "webserver": "Unicorn", - "os": "Linux", - "database_os": "Linux", - "display_name": "sinatra-unicorn-mri", - "versus": "rack-unicorn-mri", - "notes": "" - }, "postgres-unicorn-mri": { "db_url": "/db", "query_url": "/queries?queries=", diff --git a/frameworks/Ruby/sinatra/boot.rb b/frameworks/Ruby/sinatra/boot.rb index 355fc53df74..97fa0c40bdb 100644 --- a/frameworks/Ruby/sinatra/boot.rb +++ b/frameworks/Ruby/sinatra/boot.rb @@ -58,8 +58,18 @@ def connect(dbtype) class World < ActiveRecord::Base self.table_name = name - alias_attribute(:randomnumber, :randomNumber) \ - if connection.adapter_name.downcase.start_with?('mysql') + alias_attribute(:randomNumber, :randomnumber) \ + if connection.adapter_name.downcase.start_with?('postgres') + + if connection.adapter_name.downcase.start_with?('mysql') + def self.upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil) + # On MySQL Batch updates verification isn't supported yet by TechEmpower. + # https://github.com/TechEmpower/FrameworkBenchmarks/issues/5983 + attributes.each do |attrs| + where(id: attrs[:id]).update_all(randomNumber: attrs[:randomNumber]) + end + end + end end class Fortune < ActiveRecord::Base diff --git a/frameworks/Ruby/sinatra/hello_world.rb b/frameworks/Ruby/sinatra/hello_world.rb index 90238498ed2..dad43a41c20 100644 --- a/frameworks/Ruby/sinatra/hello_world.rb +++ b/frameworks/Ruby/sinatra/hello_world.rb @@ -39,19 +39,15 @@ def rand1 response['Server'] = SERVER_STRING end if SERVER_STRING - after do - ActiveRecord::Base.connection_handler.clear_active_connections! - end - # Test type 1: JSON serialization get '/json' do - json :message=>'Hello, World!' + json message: 'Hello, World!' end # Test type 2: Single database query get '/db' do world = - ActiveRecord::Base.connection_pool.with_connection do + ActiveRecord::Base.with_connection do World.find(rand1).attributes end @@ -61,7 +57,7 @@ def rand1 # Test type 3: Multiple database queries get '/queries' do worlds = - ActiveRecord::Base.connection_pool.with_connection do + ActiveRecord::Base.with_connection do ALL_IDS.sample(bounded_queries).map do |id| World.find(id).attributes end @@ -72,31 +68,33 @@ def rand1 # Test type 4: Fortunes get '/fortunes' do - @fortunes = ActiveRecord::Base.connection_pool.with_connection do + @fortunes = ActiveRecord::Base.with_connection do Fortune.all end.to_a @fortunes << Fortune.new( - :id=>0, - :message=>'Additional fortune added at request time.' + id: 0, + message: 'Additional fortune added at request time.' ) @fortunes.sort_by!(&:message) - erb :fortunes, :layout=>true + erb :fortunes, layout: true end # Test type 5: Database updates get '/updates' do - worlds = - ActiveRecord::Base.connection_pool.with_connection do - ALL_IDS.sample(bounded_queries).map do |id| - world = World.find(id) - new_value = rand1 - new_value = rand1 while new_value == world.randomnumber - world.update_columns(randomnumber: new_value) - world.attributes - end + worlds = nil + ActiveRecord::Base.with_connection do + worlds = ALL_IDS.sample(bounded_queries).map do |id| + world = World.find(id) + new_value = rand1 + new_value = rand1 until new_value != world.randomNumber + { id: id, randomNumber: new_value } end - + end + worlds.sort_by!{_1[:id]} + ActiveRecord::Base.with_connection do + World.upsert_all(worlds) + end json worlds end diff --git a/frameworks/Ruby/sinatra/sinatra-passenger-mri.dockerfile b/frameworks/Ruby/sinatra/sinatra-passenger-mri.dockerfile deleted file mode 100644 index df0ddd1fa46..00000000000 --- a/frameworks/Ruby/sinatra/sinatra-passenger-mri.dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM ruby:3.3 - -ENV RUBY_YJIT_ENABLE=1 - -# Use Jemalloc -RUN apt-get update && \ - apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 - -ADD ./ /sinatra -WORKDIR /sinatra - -RUN bundle install --jobs=4 --gemfile=/sinatra/Gemfile - -# TODO: https://github.com/phusion/passenger/issues/1916 -ENV _PASSENGER_FORCE_HTTP_SESSION=true -ENV DBTYPE=mysql - -RUN ruby -r /sinatra/config/auto_tune -e 'puts auto_tune.first' > instances - -EXPOSE 8080 - -CMD bundle exec passenger start --log-level 1 \ - --engine builtin --disable-turbocaching --disable-security-update-check \ - --spawn-method direct --max-pool-size $(cat instances) --min-instances $(cat instances) --max-request-queue-size 1024 \ - --address 0.0.0.0 --port 8080 --environment production diff --git a/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile b/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile index 0ad6b16252b..5d1f93bcb10 100644 --- a/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile @@ -1,11 +1,11 @@ -FROM ruby:3.3 +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=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra diff --git a/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile b/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile index a38a0456218..027a5593040 100644 --- a/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile @@ -5,7 +5,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra diff --git a/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile b/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile index 69ec07037dd..6258ac08c7d 100644 --- a/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile @@ -5,7 +5,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra diff --git a/frameworks/Ruby/sinatra/sinatra-unicorn-mri.dockerfile b/frameworks/Ruby/sinatra/sinatra-unicorn-mri.dockerfile deleted file mode 100644 index 76ca52267f1..00000000000 --- a/frameworks/Ruby/sinatra/sinatra-unicorn-mri.dockerfile +++ /dev/null @@ -1,19 +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=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 - -ADD ./ /sinatra -WORKDIR /sinatra - -RUN bundle install --jobs=4 --gemfile=/sinatra/Gemfile - -ENV DBTYPE=mysql - -EXPOSE 8080 - -CMD bundle exec unicorn -c config/mri_unicorn.rb -o 0.0.0.0 -p 8080 -E production diff --git a/frameworks/Ruby/sinatra/sinatra.dockerfile b/frameworks/Ruby/sinatra/sinatra.dockerfile index 85fe69e9b73..8e204def810 100644 --- a/frameworks/Ruby/sinatra/sinatra.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra.dockerfile @@ -5,7 +5,7 @@ ENV RUBY_YJIT_ENABLE=1 # Use Jemalloc RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra diff --git a/frameworks/Rust/axum/Cargo.lock b/frameworks/Rust/axum/Cargo.lock index 3d63ad71fb7..8f756182439 100644 --- a/frameworks/Rust/axum/Cargo.lock +++ b/frameworks/Rust/axum/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -17,11 +17,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", @@ -32,18 +38,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -70,15 +76,26 @@ dependencies = [ "yansi-term", ] +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-trait" -version = "0.1.75" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] @@ -91,61 +108,32 @@ dependencies = [ ] [[package]] -name = "atomic-write-file" -version = "0.1.2" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" -dependencies = [ - "nix", - "rand", -] +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "axum" -version = "0.2.0" -dependencies = [ - "axum 0.6.20", - "deadpool", - "deadpool-postgres", - "dotenv", - "futures", - "futures-util", - "hyper", - "mongodb", - "num_cpus", - "rand", - "serde", - "serde_json", - "sqlx", - "tokio", - "tokio-pg-mapper", - "tokio-pg-mapper-derive", - "tokio-postgres", - "tower", - "tower-http", - "yarte", -] +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", "bytes", "futures-util", "http", "http-body", + "http-body-util", "hyper", + "hyper-util", "itoa", "matchit", "memchr", @@ -157,41 +145,78 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" dependencies = [ "async-trait", "bytes", "futures-util", "http", "http-body", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", ] +[[package]] +name = "axum-techempower" +version = "0.2.1" +dependencies = [ + "axum", + "axum-core", + "bytes", + "deadpool", + "deadpool-postgres", + "dotenv", + "futures", + "futures-util", + "hyper", + "hyper-util", + "mime", + "moka", + "mongodb", + "num_cpus", + "rand", + "serde", + "serde_json", + "serde_path_to_error", + "simd-json", + "socket2 0.5.7", + "sqlx", + "tokio", + "tokio-pg-mapper", + "tokio-pg-mapper-derive", + "tokio-postgres", + "tower 0.5.1", + "tower-http", + "yarte", +] + [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -204,9 +229,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -222,9 +253,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -252,15 +283,15 @@ dependencies = [ [[package]] name = "bson" -version = "2.8.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c18b51216e1f74b9d769cead6ace2f82b965b807e3d73330aabe9faec31c84" +checksum = "d8a88e82b9106923b5c4d6edfca9e7db958d4e98a478ec115022e81b9b38e2c8" dependencies = [ "ahash", "base64 0.13.1", "bitvec", "hex", - "indexmap 1.9.3", + "indexmap", "js-sys", "once_cell", "rand", @@ -273,9 +304,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -285,18 +316,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -307,14 +339,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.48.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", ] [[package]] @@ -329,36 +370,26 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] [[package]] name = "crc" -version = "3.0.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -371,32 +402,46 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] -name = "crossbeam-queue" -version = "0.3.9" +name = "crossbeam-channel" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] -name = "crossbeam-utils" -version = "0.8.17" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "cfg-if", + "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crypto-common" version = "0.1.6" @@ -444,17 +489,16 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "deadpool" -version = "0.10.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed" dependencies = [ - "async-trait", "deadpool-runtime", "num_cpus", "serde", @@ -463,11 +507,14 @@ dependencies = [ [[package]] name = "deadpool-postgres" -version = "0.12.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda39fa1cfff190d8924d447ad04fd22772c250438ca5ce1dfb3c80621c05aaa" +checksum = "1ab8a4ea925ce79678034870834602a2980f4b88c09e97feb266496dbb4493d2" dependencies = [ + "async-trait", "deadpool", + "getrandom", + "serde", "tokio", "tokio-postgres", "tracing", @@ -475,18 +522,18 @@ dependencies = [ [[package]] name = "deadpool-runtime" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" dependencies = [ "tokio", ] [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", @@ -495,9 +542,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] @@ -515,15 +562,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 1.0.109", + "syn 2.0.75", ] [[package]] @@ -558,9 +605,9 @@ checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" dependencies = [ "serde", ] @@ -585,9 +632,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -610,6 +657,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -618,24 +686,27 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] -name = "finl_unicode" -version = "1.2.0" +name = "flate2" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.0", +] [[package]] -name = "flate2" -version = "1.0.28" +name = "float-cmp" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ - "crc32fast", - "miniz_oxide", + "num-traits", ] [[package]] @@ -646,7 +717,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -655,21 +726,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -687,9 +743,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -702,9 +758,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -712,15 +768,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -740,38 +796,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -797,32 +853,57 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] -name = "hashbrown" -version = "0.12.3" +name = "h2" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "halfbrown" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" +dependencies = [ + "hashbrown", + "serde", +] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -834,7 +915,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -848,9 +929,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -898,9 +979,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -909,26 +990,32 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", - "pin-project-lite", ] [[package]] -name = "http-range-header" -version = "0.3.1" +name = "http-body-util" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -938,32 +1025,46 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.28" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", "tokio", + "tower 0.4.13", "tower-service", - "tracing", - "want", ] [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1011,22 +1112,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -1035,7 +1126,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.5", + "socket2 0.5.7", "widestring", "windows-sys 0.48.0", "winreg", @@ -1047,53 +1138,108 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "itertools" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" -dependencies = [ - "either", -] - [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lexical-core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" dependencies = [ - "spin 0.5.2", + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +dependencies = [ + "lexical-util", + "lexical-write-integer", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +dependencies = [ + "lexical-util", + "static_assertions", ] [[package]] name = "libc" -version = "0.2.151" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libm" @@ -1120,15 +1266,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1136,9 +1282,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru-cache" @@ -1179,9 +1325,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -1197,29 +1343,63 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" -version = "0.8.10" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "moka" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" +dependencies = [ + "async-lock", + "async-trait", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "event-listener 5.3.1", + "futures-util", + "once_cell", + "parking_lot", + "quanta", + "rustc_version 0.4.0", + "smallvec", + "tagptr", + "thiserror", + "triomphe", + "uuid", ] [[package]] name = "mongodb" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c30763a5c6c52079602be44fa360ca3bfacee55fca73f4734aecd23706a7f2" +checksum = "ef206acb1b72389b49bc9985efe7eb1f8a9bb18e5680d262fac26c07f44025f1" dependencies = [ "async-trait", "base64 0.13.1", @@ -1251,47 +1431,18 @@ dependencies = [ "snap", "socket2 0.4.10", "stringprep", - "strsim", - "take_mut", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-util", - "trust-dns-proto", - "trust-dns-resolver", - "typed-builder", - "uuid", - "webpki-roots", - "zstd", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "libc", + "strsim", + "take_mut", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "trust-dns-proto", + "trust-dns-resolver", + "typed-builder", + "uuid", + "webpki-roots", + "zstd", ] [[package]] @@ -1321,21 +1472,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -1344,9 +1500,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -1364,9 +1520,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -1378,54 +1534,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "openssl" -version = "0.10.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" -dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.42", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.97" +name = "parking" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1433,22 +1551,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" @@ -1494,29 +1612,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1547,17 +1665,17 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "postgres-protocol" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" +checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" dependencies = [ - "base64 0.21.5", + "base64 0.22.1", "byteorder", "bytes", "fallible-iterator", @@ -1571,9 +1689,9 @@ dependencies = [ [[package]] name = "postgres-types" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" +checksum = "02048d9e032fb3cc3413bbf7b83a15d84a5d419778e2628751896d856498eee9" dependencies = [ "bytes", "fallible-iterator", @@ -1588,9 +1706,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" @@ -1604,13 +1725,28 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] +[[package]] +name = "quanta" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1619,9 +1755,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1662,6 +1798,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "raw-cpuid" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1671,11 +1816,40 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.75", +] + [[package]] name = "regex" -version = "1.10.2" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1685,9 +1859,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1696,9 +1870,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "resolv-conf" @@ -1712,16 +1886,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", - "spin 0.9.8", + "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1746,9 +1921,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" @@ -1765,7 +1940,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.20", + "semver 1.0.23", ] [[package]] @@ -1780,11 +1955,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1793,9 +1968,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.11" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", @@ -1809,7 +1984,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -1824,24 +1999,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys 0.48.0", -] +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -1859,29 +2025,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "0.9.0" @@ -1893,9 +2036,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "semver-parser" @@ -1905,50 +2048,51 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ - "indexmap 2.1.0", + "indexmap", "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_path_to_error" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -2021,11 +2165,17 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -2040,6 +2190,28 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-json" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570c430b3d902ea083097e853263ae782dfe40857d93db019a12356c8e8143fa" +dependencies = [ + "getrandom", + "halfbrown", + "lexical-core", + "ref-cast", + "serde", + "serde_json", + "simdutf8", + "value-trait", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "siphasher" version = "0.3.11" @@ -2057,9 +2229,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snap" @@ -2079,20 +2251,14 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -2114,20 +2280,19 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "itertools", "nom", "unicode_categories", ] [[package]] name = "sqlx" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" dependencies = [ "sqlx-core", "sqlx-macros", @@ -2138,9 +2303,9 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" dependencies = [ "ahash", "atoi", @@ -2148,9 +2313,8 @@ dependencies = [ "bytes", "crc", "crossbeam-queue", - "dotenvy", "either", - "event-listener", + "event-listener 2.5.3", "futures-channel", "futures-core", "futures-intrusive", @@ -2158,13 +2322,14 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap 2.1.0", + "indexmap", "log", "memchr", - "native-tls", "once_cell", "paste", "percent-encoding", + "rustls", + "rustls-pemfile", "serde", "serde_json", "sha2", @@ -2175,13 +2340,14 @@ dependencies = [ "tokio-stream", "tracing", "url", + "webpki-roots", ] [[package]] name = "sqlx-macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" dependencies = [ "proc-macro2", "quote", @@ -2192,11 +2358,10 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" dependencies = [ - "atomic-write-file", "dotenvy", "either", "heck", @@ -2219,13 +2384,13 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ "atoi", - "base64 0.21.5", - "bitflags 2.4.1", + "base64 0.21.7", + "bitflags 2.6.0", "byteorder", "bytes", "crc", @@ -2261,13 +2426,13 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi", - "base64 0.21.5", - "bitflags 2.4.1", + "base64 0.21.7", + "bitflags 2.6.0", "byteorder", "crc", "dotenvy", @@ -2288,7 +2453,6 @@ dependencies = [ "rand", "serde", "serde_json", - "sha1", "sha2", "smallvec", "sqlx-core", @@ -2300,9 +2464,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" dependencies = [ "atoi", "flume", @@ -2321,15 +2485,21 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stringprep" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] [[package]] @@ -2340,9 +2510,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -2357,9 +2527,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", @@ -2372,6 +2542,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "take_mut" version = "0.2.2" @@ -2386,45 +2568,46 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "once_cell", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -2439,18 +2622,19 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -2463,32 +2647,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.7", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] @@ -2513,9 +2696,9 @@ dependencies = [ [[package]] name = "tokio-postgres" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" +checksum = "03adcf0147e203b6032c0b2d30be1415ba03bc348901f3ff1cc0df6a733e60c3" dependencies = [ "async-trait", "byteorder", @@ -2531,7 +2714,7 @@ dependencies = [ "postgres-protocol", "postgres-types", "rand", - "socket2 0.5.5", + "socket2 0.5.7", "tokio", "tokio-util", "whoami", @@ -2549,9 +2732,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -2560,9 +2743,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -2570,7 +2753,6 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -2595,22 +2777,34 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] -name = "tower-http" -version = "0.4.4" +name = "tower" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ - "bitflags 2.4.1", - "bytes", "futures-core", "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.6.0", + "bytes", "http", "http-body", - "http-range-header", + "http-body-util", "pin-project-lite", "tower-layer", "tower-service", @@ -2618,15 +2812,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -2648,7 +2842,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] @@ -2660,6 +2854,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" + [[package]] name = "trust-dns-proto" version = "0.21.2" @@ -2705,12 +2905,6 @@ dependencies = [ "trust-dns-proto", ] -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "typed-builder" version = "0.10.0" @@ -2730,9 +2924,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2742,30 +2936,36 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" + [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "unicode_categories" @@ -2781,9 +2981,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -2798,9 +2998,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "serde", @@ -2822,6 +3022,18 @@ version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c" +[[package]] +name = "value-trait" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad8db98c1e677797df21ba03fca7d3bf9bec3ca38db930954e4fe6e1ea27eb4" +dependencies = [ + "float-cmp", + "halfbrown", + "itoa", + "ryu", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -2830,18 +3042,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -2857,34 +3060,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2892,28 +3096,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -2921,9 +3125,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "whoami" @@ -2931,16 +3135,16 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "redox_syscall", + "redox_syscall 0.4.1", "wasite", "web-sys", ] [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -2966,11 +3170,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2988,7 +3192,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -3008,17 +3221,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -3029,9 +3243,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -3041,9 +3255,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -3053,9 +3267,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -3065,9 +3285,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -3077,9 +3297,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -3089,9 +3309,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -3101,9 +3321,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winreg" @@ -3220,29 +3440,30 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.75", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zstd" @@ -3265,9 +3486,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/frameworks/Rust/axum/Cargo.toml b/frameworks/Rust/axum/Cargo.toml index f4ecf45adf4..a47514726c7 100644 --- a/frameworks/Rust/axum/Cargo.toml +++ b/frameworks/Rust/axum/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "axum" -version = "0.2.0" +name = "axum-techempower" +version = "0.2.1" authors = ["Dragos Varovici "] edition = "2021" @@ -28,28 +28,63 @@ path = "src/main_mongo_raw.rs" name = "axum-pg" path = "src/main_pg.rs" +[features] +default = [] +simd-json = [ + "dep:simd-json", + "dep:axum-core", + "dep:mime", + "dep:bytes", + "dep:serde_path_to_error", +] + [dependencies] -axum = { version = "0.6.16", default-features = false, features = ["json", "query", "http1", "tokio"] } -deadpool = { version = "0.10.0", features = ["rt_tokio_1", "serde", "async-trait", "managed" ] } -deadpool-postgres = "0.12.1" +axum = { version = "0.7.6", default-features = false, features = [ + "json", + "query", + "http1", + "tokio", +] } +deadpool = { version = "0.12.1", features = ["rt_tokio_1", "serde", "managed"] } +deadpool-postgres = { version = "0.14.0", features = ["rt_tokio_1", "serde"] } dotenv = "0.15.0" -futures = "0.3.25" -futures-util = "0.3.25" -hyper = { version = "0.14.23", features = ["http1", "server"] } -mongodb = { version = "2.3.1", features = ["zstd-compression", "snappy-compression", "zlib-compression"] } -num_cpus = "1.14.0" +futures = "0.3.30" +futures-util = "0.3.30" +mongodb = { version = "2.8.0", features = [ + "zstd-compression", + "snappy-compression", + "zlib-compression", +] } +num_cpus = "1.16.0" rand = { version = "0.8.5", features = ["small_rng"] } -serde = { version = "1.0.149", features = ["derive"] } -serde_json = "1.0.89" -sqlx = { version = "0.7.3", features = ["postgres", "macros", "runtime-tokio-native-tls"] } -tokio = { version = "1.24.2", features = ["full"] } -tokio-pg-mapper = "0.2.0" -tokio-pg-mapper-derive = "0.2.0" -tokio-postgres = "0.7.7" -tower = { version = "0.4.13", features = ["util"] } -tower-http = { version = "0.4.0", features = ["set-header"] } +serde = { version = "1.0.196", features = ["derive"] } +serde_json = "1.0.127" +sqlx = { version = "0.7.3", features = [ + "postgres", + "macros", + "runtime-tokio", + "tls-rustls", +] } +tokio = { version = "1.39.3", features = ["full"] } +tokio-pg-mapper = { version = "0.2.0" } +tokio-pg-mapper-derive = { version = "0.2.0" } +tokio-postgres = { version = "0.7.11" } +tower = { version = "0.5.0", features = ["util"] } +tower-http = { version = "0.5.2", features = ["set-header"] } yarte = "0.15.7" +simd-json = { version = "0.13.8", optional = true } +axum-core = { version = "0.4.3", optional = true } +mime = { version = "0.3.17", optional = true } +bytes = { version = "1.5.0", optional = true } +serde_path_to_error = { version = "0.1.15", optional = true } +moka = { version = "0.12.8", features = ["future"] } +socket2 = "0.5.7" +hyper = { version = "1.4", features = ["server", "http1"] } +hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] } + [profile.release] -lto = true +lto = "fat" codegen-units = 1 +strip = true +opt-level = 3 diff --git a/frameworks/Rust/axum/README.md b/frameworks/Rust/axum/README.md index 55ca3e0978e..59979fe441d 100755 --- a/frameworks/Rust/axum/README.md +++ b/frameworks/Rust/axum/README.md @@ -1,42 +1,46 @@ - -# [Axum](https://github.com/tokio-rs/axum) - Ergonomic and modular web framework built with Tokio, Tower, and Hyper +# [Axum](https://github.com/tokio-rs/axum) ## Description -Axum is a web application framework that focuses on ergonomics and modularity. - -* [User Guide](https://docs.rs/axum/0.3/axum/) -* [API Documentation](https://docs.rs/axum/0.3/axum/) -* Cargo package: [axum](https://crates.io/crates/axum) +Axum is a web application framework that focuses on ergonomics and modularity, +built with Tokio, Tower, and Hyper. -## Database +- [User Guide](https://docs.rs/axum/latest/axum/) +- [API Documentation](https://docs.rs/axum/latest/axum/) +- [Cargo Package (`axum`)](https://crates.io/crates/axum) -PostgreSQL +## Variants -* Raw using [sqlx](https://github.com/launchbadge/sqlx) +- PostgreSQL using `SQLx`, `tokio_postgres`, and `deadpool`. +- MongoDB with `mongodb`. ## Test URLs -### Test 1: JSON Encoding - - http://localhost:8000/json - -### Test 2: Single Row Query - - http://localhost:8000/db - -### Test 3: Multi Row Query - - http://localhost:8000/queries?q=20 - -### Test 4: Fortunes (Template rendering) - - http://localhost:8000/fortunes - -### Test 5: Update Query - - http://localhost:8000/updates?q=20 - -### Test 6: Plaintext - - http://localhost:8000/plaintext +- Plaintext: http://localhost:8000/plaintext +- JSON Encoding: http://localhost:8000/json +- Single Row Query: http://localhost:8000/db +- Multi Row Query: http://localhost:8000/queries?q=20 +- Fortunes: http://localhost:8000/fortunes +- Update Query: http://localhost:8000/updates?q=20 +- Cached Query: http://localhost:8000/cached-queries?q=20 + +## Notable Points (both performance and build) + +- Use of `async`. +- Use of most recent versions of Rust, `axum` and dependencies. +- (Disabled by default) Compile-time swap-in of `simd-json` instead of `serde_json` for faster JSON serialization. +- Release binaries are stripped and compiled with CPU native. +- Sockets configured with TCP_NODELAY and to support an increased number of pending connections. +- Server configured to serve HTTP/1 only, with no need for websockets. +- Separation of build and deployment containers using multi-stage builds. +- Deployment into Google's minimal `distroless-cc` container. +- Use of pipelined database queries (where supported). +- Streaming database queries (where supported). +- Use of PostgreSQL prepared statements cache (where supported). +- Use of PostgreSQL arrays to execute multi-row database updates with a single `UPDATE` query. + - This is permitted by the [test requirements](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates), step (ix). +- In version 0.7.6 (as yet unreleased), a native API to set TCP_NODELAY will be included. + - https://github.com/tokio-rs/axum/pull/2653/ + - https://github.com/tokio-rs/axum/issues/2521 +- More performance improvements are to be expected in version 0.8: + - https://github.com/tokio-rs/axum/issues/1827 diff --git a/frameworks/Rust/axum/axum-mongo-raw.dockerfile b/frameworks/Rust/axum/axum-mongo-raw.dockerfile deleted file mode 100644 index ffdabfc2d8c..00000000000 --- a/frameworks/Rust/axum/axum-mongo-raw.dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM rust:1.75-slim-buster - -ENV AXUM_TECHEMPOWER_MONGODB_URL=mongodb://tfb-database:27017 -ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=28 -ENV AXUM_TECHEMPOWER_MIN_POOL_SIZE=14 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - pkg-config libssl-dev \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /axum -COPY ./src ./src -COPY ./templates ./templates -COPY ./Cargo.toml ./Cargo.toml -COPY ./Cargo.lock ./Cargo.lock -COPY ./run.sh ./run.sh -RUN chmod +x ./run.sh - -ENV RUSTFLAGS "-C target-cpu=native" -RUN cargo build --release -RUN cp ./target/release/axum-mongo-raw ./target/release/axum-techempower - -EXPOSE 8000 - -CMD ["./run.sh"] diff --git a/frameworks/Rust/axum/axum-mongo.dockerfile b/frameworks/Rust/axum/axum-mongo.dockerfile deleted file mode 100644 index 99a6f76f997..00000000000 --- a/frameworks/Rust/axum/axum-mongo.dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM rust:1.75-slim-buster - -ENV AXUM_TECHEMPOWER_MONGODB_URL=mongodb://tfb-database:27017 -ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=28 -ENV AXUM_TECHEMPOWER_MIN_POOL_SIZE=14 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - pkg-config libssl-dev \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /axum -COPY ./src ./src -COPY ./templates ./templates -COPY ./Cargo.toml ./Cargo.toml -COPY ./Cargo.lock ./Cargo.lock -COPY ./run.sh ./run.sh -RUN chmod +x ./run.sh - -ENV RUSTFLAGS "-C target-cpu=native" -RUN cargo build --release -RUN cp ./target/release/axum-mongo ./target/release/axum-techempower - -EXPOSE 8000 - -CMD ["./run.sh"] diff --git a/frameworks/Rust/axum/axum-pg-pool.dockerfile b/frameworks/Rust/axum/axum-pg-pool.dockerfile deleted file mode 100644 index c836faa2381..00000000000 --- a/frameworks/Rust/axum/axum-pg-pool.dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM rust:1.75-slim-buster - -ENV AXUM_TECHEMPOWER_DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world -ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=28 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libpq-dev pkg-config libssl-dev \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /axum -COPY ./src ./src -COPY ./templates ./templates -COPY ./Cargo.toml ./Cargo.toml -COPY ./Cargo.lock ./Cargo.lock -COPY ./run.sh ./run.sh -RUN chmod +x ./run.sh - -ENV RUSTFLAGS "-C target-cpu=native" -RUN cargo build --release -RUN cp ./target/release/axum-pg-pool ./target/release/axum-techempower - -EXPOSE 8000 - -CMD ["./run.sh"] diff --git a/frameworks/Rust/axum/axum-pg.dockerfile b/frameworks/Rust/axum/axum-pg.dockerfile deleted file mode 100644 index 98d44481007..00000000000 --- a/frameworks/Rust/axum/axum-pg.dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM rust:1.75-slim-buster - -ENV AXUM_TECHEMPOWER_DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libpq-dev pkg-config libssl-dev \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /axum -COPY ./src ./src -COPY ./templates ./templates -COPY ./Cargo.toml ./Cargo.toml -COPY ./Cargo.lock ./Cargo.lock -COPY ./run.sh ./run.sh -RUN chmod +x ./run.sh - -ENV RUSTFLAGS "-C target-cpu=native" -RUN cargo build --release -RUN cp ./target/release/axum-pg ./target/release/axum-techempower - -EXPOSE 8000 - -CMD ["./run.sh"] diff --git a/frameworks/Rust/axum/axum-sqlx.dockerfile b/frameworks/Rust/axum/axum-sqlx.dockerfile deleted file mode 100644 index 0cecda65f32..00000000000 --- a/frameworks/Rust/axum/axum-sqlx.dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM rust:1.75-slim-buster - -ENV AXUM_TECHEMPOWER_DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world -ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=56 -ENV AXUM_TECHEMPOWER_MIN_POOL_SIZE=56 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libpq-dev pkg-config libssl-dev \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /axum -COPY ./src ./src -COPY ./templates ./templates -COPY ./Cargo.toml ./Cargo.toml -COPY ./Cargo.lock ./Cargo.lock -COPY ./run.sh ./run.sh -RUN chmod +x ./run.sh - -ENV RUSTFLAGS "-C target-cpu=native" -RUN cargo build --release -RUN cp ./target/release/axum-sqlx ./target/release/axum-techempower - - -EXPOSE 8000 - -CMD ["./run.sh"] diff --git a/frameworks/Rust/axum/axum.dockerfile b/frameworks/Rust/axum/axum.dockerfile index 2cd772a3426..044e68bbf02 100644 --- a/frameworks/Rust/axum/axum.dockerfile +++ b/frameworks/Rust/axum/axum.dockerfile @@ -1,21 +1,24 @@ -FROM rust:1.75-slim-buster +FROM docker.io/rust:1.80-slim-bookworm AS builder RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config libssl-dev \ && rm -rf /var/lib/apt/lists/* -WORKDIR /axum -COPY ./src ./src -COPY ./templates ./templates -COPY ./Cargo.toml ./Cargo.toml -COPY ./Cargo.lock ./Cargo.lock -COPY ./run.sh ./run.sh -RUN chmod +x ./run.sh - +WORKDIR /build +COPY ./Cargo.toml ./Cargo.lock /build/ +RUN cargo fetch +COPY ./templates/ /build/templates +COPY ./src/ /build/src ENV RUSTFLAGS "-C target-cpu=native" RUN cargo build --release -RUN cp ./target/release/axum ./target/release/axum-techempower +FROM gcr.io/distroless/cc-debian12 +ENV POSTGRES_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world +ENV POSTGRES_MIN_POOL_SIZE=56 +ENV POSTGRES_MAX_POOL_SIZE=56 +ENV MONGODB_URL=mongodb://tfb-database:27017 +ENV MONGODB_MIN_POOL_SIZE=28 +ENV MONGODB_MAX_POOL_SIZE=14 +COPY --from=builder /build/target/release/axum* /app/ EXPOSE 8000 - -CMD ["./run.sh"] +CMD ["/app/axum"] diff --git a/frameworks/Rust/axum/benchmark_config.json b/frameworks/Rust/axum/benchmark_config.json index f05dd4a67dd..c33a1edad78 100755 --- a/frameworks/Rust/axum/benchmark_config.json +++ b/frameworks/Rust/axum/benchmark_config.json @@ -3,6 +3,8 @@ "tests": [ { "default": { + "dockerfile": "axum.dockerfile", + "docker_cmd": "/app/axum", "json_url": "/json", "plaintext_url": "/plaintext", "port": 8000, @@ -22,8 +24,11 @@ "versus": "None" }, "sqlx": { + "dockerfile": "axum.dockerfile", + "docker_cmd": "/app/axum-sqlx", "db_url": "/db", "fortune_url": "/fortunes", + "cached_query_url": "/cached-queries?q=", "port": 8000, "approach": "Realistic", "classification": "Fullstack", @@ -41,10 +46,12 @@ "versus": "None" }, "pg": { + "dockerfile": "axum.dockerfile", + "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", @@ -62,9 +69,11 @@ "versus": "None" }, "pg-pool": { + "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", @@ -83,10 +92,12 @@ "versus": "None" }, "mongo": { + "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", @@ -104,9 +115,11 @@ "versus": "None" }, "mongo-raw": { + "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/config.toml b/frameworks/Rust/axum/config.toml index d4bb9d7ad0e..b5695c19bf0 100644 --- a/frameworks/Rust/axum/config.toml +++ b/frameworks/Rust/axum/config.toml @@ -8,6 +8,7 @@ urls.db = "/db" urls.query = "/queries?q=" urls.update = "/updates?q=" urls.fortune = "/fortunes" +urls.cached_query = "/cached-queries?q=" approach = "Realistic" classification = "Fullstack" database = "Postgres" @@ -16,4 +17,4 @@ os = "Linux" orm = "Raw" platform = "Rust" webserver = "Hyper" -versus = "None" \ No newline at end of file +versus = "None" diff --git a/frameworks/Rust/axum/src/common/mod.rs b/frameworks/Rust/axum/src/common/mod.rs new file mode 100644 index 00000000000..808b2a70eeb --- /dev/null +++ b/frameworks/Rust/axum/src/common/mod.rs @@ -0,0 +1,51 @@ +use std::{env, str::FromStr}; + +use core::fmt::Debug; +use rand::{distributions::Uniform, rngs::SmallRng, Rng}; +pub mod models; +pub mod utils; + +#[cfg(feature = "simd-json")] +pub mod simd_json; + +#[allow(dead_code)] +pub const SELECT_ALL_FORTUNES: &str = "SELECT * FROM fortune"; +#[allow(dead_code)] +pub const SELECT_WORLD_BY_ID: &str = + "SELECT id, randomnumber FROM world WHERE id = $1 LIMIT 1"; +#[allow(dead_code)] +pub const SELECT_ALL_CACHED_WORLDS: &str = + "SELECT id, randomnumber FROM world ORDER BY id"; +#[allow(dead_code)] +pub const UPDATE_WORLDS: &str = "WITH vals AS (SELECT * FROM UNNEST($1::int[], $2::int[]) AS v(id, rnum)) + UPDATE world SET randomnumber = new.rnum FROM + (SELECT w.id, v.rnum FROM world w INNER JOIN vals v ON v.id = w.id ORDER BY w.id FOR UPDATE) AS new + WHERE world.id = new.id"; + +/// Return the value of an environment variable. +#[allow(dead_code)] +pub fn get_env(key: &str) -> T +where + ::Err: Debug, +{ + env::var(key) + .unwrap_or_else(|_| panic!("{key} environment variable was not set")) + .parse::() + .unwrap_or_else(|_| panic!("could not parse {key}")) +} + +/// Generate a single integer in the range 1 to 10,000 (inclusive) +#[allow(dead_code)] +#[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(always)] +pub fn random_ids(rng: &mut SmallRng, count: usize) -> Vec { + rng.sample_iter(Uniform::new(1, 10_001)) + .take(count) + .collect() +} diff --git a/frameworks/Rust/axum/src/models_mongo.rs b/frameworks/Rust/axum/src/common/models.rs similarity index 85% rename from frameworks/Rust/axum/src/models_mongo.rs rename to frameworks/Rust/axum/src/common/models.rs index 31c76cc7c07..1644447c3cd 100644 --- a/frameworks/Rust/axum/src/models_mongo.rs +++ b/frameworks/Rust/axum/src/common/models.rs @@ -1,5 +1,10 @@ use serde::{Deserialize, Serialize}; +#[derive(Serialize)] +pub struct Message { + pub message: &'static str, +} + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct Fortune { pub id: i32, diff --git a/frameworks/Rust/axum/src/common/simd_json.rs b/frameworks/Rust/axum/src/common/simd_json.rs new file mode 100644 index 00000000000..e8a2aafeee1 --- /dev/null +++ b/frameworks/Rust/axum/src/common/simd_json.rs @@ -0,0 +1,152 @@ +use axum::extract::rejection::JsonRejection::MissingJsonContentType; +use axum::extract::Request; +use axum::extract::{rejection::*, FromRequest}; +use axum::{async_trait, http}; +use axum_core::response::{IntoResponse, Response}; +use bytes::{BufMut, Bytes, BytesMut}; +use http::{ + header::{self, HeaderMap, HeaderValue}, + StatusCode, +}; +use serde::{de::DeserializeOwned, Serialize}; +use simd_json; + +#[derive(Debug, Clone, Copy, Default)] +pub struct Json(pub T); + +pub enum SimdJsonRejection { + Json(JsonRejection), + Bytes(BytesRejection), + Simd(String), +} + +impl IntoResponse for SimdJsonRejection { + fn into_response(self) -> Response { + todo!() + } +} + +impl From for SimdJsonRejection { + fn from(err: JsonRejection) -> Self { + SimdJsonRejection::Json(err) + } +} + +impl From for SimdJsonRejection { + fn from(err: BytesRejection) -> Self { + SimdJsonRejection::Bytes(err) + } +} + +impl From for SimdJsonRejection { + fn from(err: simd_json::Error) -> Self { + SimdJsonRejection::Simd(err.to_string()) + } +} + +#[async_trait] +impl FromRequest for Json +where + T: DeserializeOwned, + S: Send + Sync, +{ + type Rejection = SimdJsonRejection; + + async fn from_request(req: Request, state: &S) -> Result { + if json_content_type(req.headers()) { + let bytes = Bytes::from_request(req, state).await?; + Self::from_bytes(&bytes) + } else { + Err(SimdJsonRejection::Json(MissingJsonContentType( + axum::extract::rejection::MissingJsonContentType::default(), + ))) + } + } +} + +fn json_content_type(headers: &HeaderMap) -> bool { + let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) { + content_type + } else { + return false; + }; + + let content_type = if let Ok(content_type) = content_type.to_str() { + content_type + } else { + return false; + }; + + let mime = if let Ok(mime) = content_type.parse::() { + mime + } else { + return false; + }; + + let is_json_content_type = mime.type_() == "application" + && (mime.subtype() == "json" + || mime.suffix().map_or(false, |name| name == "json")); + + is_json_content_type +} + +axum_core::__impl_deref!(Json); + +impl From for Json { + fn from(inner: T) -> Self { + Self(inner) + } +} + +impl Json +where + T: DeserializeOwned, +{ + /// Construct a `Json` from a byte slice. Most users should prefer to use the `FromRequest` impl + /// but special cases may require first extracting a `Request` into `Bytes` then optionally + /// constructing a `Json`. + pub fn from_bytes(bytes: &[u8]) -> Result { + let body = &mut bytes.to_owned(); + let deserializer = simd_json::from_slice::(body); + + let value = match deserializer { + Ok(v) => v, + Err(err) => { + let rejection = { SimdJsonRejection::from(err) }; + return Err(rejection); + } + }; + + Ok(Json(value)) + } +} + +impl IntoResponse for Json +where + T: Serialize, +{ + fn into_response(self) -> Response { + // Use a small initial capacity of 128 bytes like serde_json::to_vec + // https://docs.rs/serde_json/1.0.82/src/serde_json/ser.rs.html#2189 + let mut buf = BytesMut::with_capacity(128).writer(); + match simd_json::to_writer(&mut buf, &self.0) { + Ok(()) => ( + [( + header::CONTENT_TYPE, + HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), + )], + buf.into_inner().freeze(), + ) + .into_response(), + Err(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + [( + header::CONTENT_TYPE, + HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()), + )], + err.to_string(), + ) + .into_response(), + } + } +} diff --git a/frameworks/Rust/axum/src/utils.rs b/frameworks/Rust/axum/src/common/utils.rs similarity index 62% rename from frameworks/Rust/axum/src/utils.rs rename to frameworks/Rust/axum/src/common/utils.rs index 89ecaeae6ab..0da86151e62 100644 --- a/frameworks/Rust/axum/src/utils.rs +++ b/frameworks/Rust/axum/src/common/utils.rs @@ -1,37 +1,20 @@ -use std::{env, fmt::Debug, str::FromStr}; - use axum::{ - body::{Bytes, Full}, + body::Bytes, http::{header, HeaderValue, StatusCode}, response::{IntoResponse, Response}, }; -use rand::{rngs::SmallRng, Rng}; use serde::Deserialize; -pub fn get_environment_variable(key: &str) -> T -where - ::Err: Debug, -{ - env::var(key) - .unwrap_or_else(|_| panic!("{key} environment variable was not set")) - .parse::() - .unwrap_or_else(|_| panic!("could not parse {key}")) -} - #[derive(Debug, Deserialize)] pub struct Params { - queries: Option, -} - -#[allow(dead_code)] -pub fn random_number(rng: &mut SmallRng) -> i32 { - (rng.gen::() % 10_000 + 1) as i32 + q: Option, } #[allow(dead_code)] -pub fn parse_params(params: Params) -> i32 { +#[inline(always)] +pub fn parse_params(params: Params) -> usize { params - .queries + .q .and_then(|q| q.parse().ok()) .unwrap_or(1) .clamp(1, 500) @@ -52,7 +35,7 @@ pub struct Utf8Html(pub T); impl IntoResponse for Utf8Html where - T: Into>, + T: Into, { fn into_response(self) -> Response { let mut res = (StatusCode::OK, self.0.into()).into_response(); diff --git a/frameworks/Rust/axum/src/database_pg.rs b/frameworks/Rust/axum/src/database_pg.rs deleted file mode 100644 index 56e07553af3..00000000000 --- a/frameworks/Rust/axum/src/database_pg.rs +++ /dev/null @@ -1,199 +0,0 @@ -use std::{collections::HashMap, convert::Infallible, fmt::Write, io, sync::Arc}; - -use axum::{async_trait, extract::FromRequestParts, http::request::Parts}; -use futures::{ - stream::futures_unordered::FuturesUnordered, FutureExt, StreamExt, TryStreamExt, -}; -use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng}; -use tokio::pin; -use tokio_postgres::{connect, types::ToSql, Client, NoTls, Statement}; - -use crate::models_pg::{Fortune, World}; - -#[derive(Debug)] -pub enum PgError { - Io(io::Error), - Pg(tokio_postgres::Error), -} - -impl From for PgError { - fn from(err: io::Error) -> Self { - PgError::Io(err) - } -} - -impl From for PgError { - fn from(err: tokio_postgres::Error) -> Self { - PgError::Pg(err) - } -} - -/// Postgres interface -pub struct PgConnection { - client: Client, - fortune: Statement, - world: Statement, - updates: HashMap, -} - -impl PgConnection { - pub async fn connect(db_url: String) -> Arc { - let (cl, conn) = connect(&db_url, NoTls) - .await - .expect("can not connect to postgresql"); - - // Spawn connection - tokio::spawn(async move { - if let Err(error) = conn.await { - eprintln!("Connection error: {error}"); - } - }); - - let fortune = cl.prepare("SELECT * FROM fortune").await.unwrap(); - let mut updates = HashMap::new(); - - for num in 1..=500u16 { - let mut pl = 1; - let mut q = String::new(); - - q.push_str("UPDATE world SET randomnumber = CASE id "); - - for _ in 1..=num { - let _ = write!(q, "when ${pl} then ${} ", pl + 1); - pl += 2; - } - - q.push_str("ELSE randomnumber END WHERE id IN ("); - - for _ in 1..=num { - let _ = write!(q, "${pl},"); - pl += 1; - } - - q.pop(); - q.push(')'); - - updates.insert(num, cl.prepare(&q).await.unwrap()); - } - - let world = cl.prepare("SELECT * FROM world WHERE id=$1").await.unwrap(); - - Arc::new(PgConnection { - client: cl, - fortune, - world, - updates, - }) - } -} - -impl PgConnection { - async fn query_one_world(&self, id: i32) -> Result { - let stream = self.client.query_raw(&self.world, &[&id]).await?; - pin!(stream); - let row = stream.next().await.unwrap()?; - Ok(World { - id: row.get(0), - randomnumber: row.get(1), - }) - } - - pub async fn get_world(&self) -> Result { - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - - let random_id = (rng.gen::() % 10_000 + 1) as i32; - - let world = self.query_one_world(random_id).await?; - Ok(world) - } - - pub async fn get_worlds(&self, num: usize) -> Result, PgError> { - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - - let worlds = FuturesUnordered::new(); - - for _ in 0..num { - let w_id = (rng.gen::() % 10_000 + 1) as i32; - worlds.push(self.query_one_world(w_id)); - } - - worlds.try_collect().await - } - - pub async fn update(&self, num: u16) -> Result, PgError> { - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - - let worlds = FuturesUnordered::new(); - - for _ in 0..num { - let id = (rng.gen::() % 10_000 + 1) as i32; - let w_id = (rng.gen::() % 10_000 + 1) as i32; - - worlds.push(self.query_one_world(w_id).map(move |res| match res { - Ok(mut world) => { - world.randomnumber = id; - Ok(world) - } - - Err(err) => Err(err), - })); - } - - let st = self.updates.get(&num).unwrap().clone(); - - let worlds: Vec = worlds.try_collect().await?; - - let mut params: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(num as usize * 3); - - for w in &worlds { - params.push(&w.id); - params.push(&w.randomnumber); - } - - for w in &worlds { - params.push(&w.id); - } - - self.client.query(&st, ¶ms[..]).await?; - - Ok(worlds) - } - - pub async fn tell_fortune(&self) -> Result, PgError> { - let mut items = vec![Fortune { - id: 0, - message: "Additional fortune added at request time.".parse().unwrap(), - }]; - - let fut = self.client.query_raw::<_, _, &[i32; 0]>(&self.fortune, &[]); - - let stream = fut.await?; - pin!(stream); - - while let Some(row) = stream.next().await { - let row = row?; - - items.push(Fortune { - id: row.get(0), - message: row.get(1), - }); - } - - items.sort_by(|it, next| it.message.cmp(&next.message)); - Ok(items) - } -} - -pub struct DatabaseConnection(pub Arc); - -#[async_trait] -impl FromRequestParts> for DatabaseConnection { - type Rejection = Infallible; - - async fn from_request_parts( - _parts: &mut Parts, - pg_connection: &Arc, - ) -> Result { - Ok(Self(pg_connection.clone())) - } -} diff --git a/frameworks/Rust/axum/src/database_sqlx.rs b/frameworks/Rust/axum/src/database_sqlx.rs deleted file mode 100644 index b156bb3a3b7..00000000000 --- a/frameworks/Rust/axum/src/database_sqlx.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::io; - -use axum::{ - async_trait, - extract::FromRequestParts, - http::{request::Parts, StatusCode}, -}; -use sqlx::{ - pool::PoolConnection, - postgres::{PgArguments, PgPoolOptions}, - Arguments, PgPool, Postgres, -}; - -use crate::{utils::internal_error, Fortune, World}; - -#[derive(Debug)] -pub enum PgError { - Io(io::Error), - Pg(sqlx::Error), -} - -impl From for PgError { - fn from(err: io::Error) -> Self { - PgError::Io(err) - } -} - -impl From for PgError { - fn from(err: sqlx::Error) -> Self { - PgError::Pg(err) - } -} - -pub async fn create_pool( - database_url: String, - max_pool_size: u32, - min_pool_size: u32, -) -> PgPool { - PgPoolOptions::new() - .max_connections(max_pool_size) - .min_connections(min_pool_size) - .connect(&database_url) - .await - .unwrap() -} - -pub struct DatabaseConnection(pub PoolConnection); - -#[async_trait] -impl FromRequestParts for DatabaseConnection { - type Rejection = (StatusCode, String); - - async fn from_request_parts( - _parts: &mut Parts, - pool: &PgPool, - ) -> Result { - let conn = pool.acquire().await.map_err(internal_error)?; - - Ok(Self(conn)) - } -} - -pub async fn fetch_world( - mut conn: PoolConnection, - number: i32, -) -> Result { - let mut args = PgArguments::default(); - args.add(number); - - let world: World = - sqlx::query_as_with("SELECT id, randomnumber FROM World WHERE id = $1", args) - .fetch_one(&mut *conn) - .await - .expect("error loading world"); - Ok(world) -} - -pub async fn fetch_fortunes( - mut conn: PoolConnection, -) -> Result, PgError> { - let fortunes: Vec = sqlx::query_as("SELECT * FROM Fortune") - .fetch_all(&mut *conn) - .await - .expect("error loading Fortunes"); - Ok(fortunes) -} diff --git a/frameworks/Rust/axum/src/main.rs b/frameworks/Rust/axum/src/main.rs index 075ee1c01e6..0b33fb7edcb 100644 --- a/frameworks/Rust/axum/src/main.rs +++ b/frameworks/Rust/axum/src/main.rs @@ -1,21 +1,23 @@ -use axum::{ - http::{header, HeaderValue, StatusCode}, - response::IntoResponse, - routing::get, - Json, Router, -}; -use dotenv::dotenv; -use tower_http::set_header::SetResponseHeaderLayer; - -mod models_common; +mod common; mod server; -use self::models_common::Message; +use axum::{http::StatusCode, response::IntoResponse, routing::get, Router}; +use common::models::Message; +use dotenv::dotenv; + +#[cfg(not(feature = "simd-json"))] +use axum::Json; +#[cfg(feature = "simd-json")] +use common::simd_json::Json; +/// Return a plaintext static string. +#[inline(always)] pub async fn plaintext() -> &'static str { "Hello, World!" } +/// Return a JSON message. +#[inline(always)] pub async fn json() -> impl IntoResponse { let message = Message { message: "Hello, World!", @@ -24,23 +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) +} - let server_header_value = HeaderValue::from_static("Axum"); +async fn serve_app() { let app = Router::new() .route("/plaintext", get(plaintext)) - .route("/json", get(json)) - .layer(SetResponseHeaderLayer::if_not_present( - header::SERVER, - server_header_value, - )); - - server::builder() - .http1_pipeline_flush(true) - .serve(app.into_make_service()) - .await - .unwrap(); -} + .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_mongo.rs b/frameworks/Rust/axum/src/main_mongo.rs index 8219113f583..6d301189b9a 100644 --- a/frameworks/Rust/axum/src/main_mongo.rs +++ b/frameworks/Rust/axum/src/main_mongo.rs @@ -1,11 +1,21 @@ +mod common; +mod mongo; +mod server; +//mod mongo_raw; + use std::time::Duration; use axum::{ - extract::Query, - http::{header, HeaderValue, StatusCode}, - response::IntoResponse, - routing::get, - Json, Router, + extract::Query, http::StatusCode, response::IntoResponse, routing::get, Router, +}; + +#[cfg(not(feature = "simd-json"))] +use axum::Json; +#[cfg(feature = "simd-json")] +use common::simd_json::Json; +use common::{ + models::{FortuneInfo, World}, + random_ids, }; use dotenv::dotenv; use mongodb::{ @@ -13,21 +23,14 @@ use mongodb::{ Client, }; use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng}; -use tower_http::set_header::SetResponseHeaderLayer; use yarte::Template; -mod database_mongo; -mod models_common; -mod models_mongo; -mod server; -mod utils; - -use self::{ - database_mongo::{ - fetch_fortunes, find_world_by_id, find_worlds, update_worlds, DatabaseConnection, - }, - models_mongo::{Fortune, FortuneInfo, World}, - utils::{get_environment_variable, parse_params, Params, Utf8Html}, +use common::{ + get_env, + utils::{parse_params, Params, Utf8Html}, +}; +use mongo::database::{ + fetch_fortunes, find_world_by_id, find_worlds, update_worlds, DatabaseConnection, }; #[derive(Template)] @@ -55,13 +58,7 @@ async fn queries( let q = parse_params(params); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let mut ids: Vec = Vec::with_capacity(q as usize); - - for _ in 0..q { - let random_id = (rng.gen::() % 10_000 + 1) as i32; - - ids.push(random_id); - } + let ids = random_ids(&mut rng, q); let worlds = find_worlds(db, ids).await; let results = worlds.expect("worlds could not be retrieved"); @@ -76,18 +73,12 @@ async fn updates( let q = parse_params(params); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let mut ids: Vec = Vec::with_capacity(q as usize); - - for _ in 0..q { - let random_id = (rng.gen::() % 10_000 + 1) as i32; - - ids.push(random_id); - } + let ids = random_ids(&mut rng, q); let worlds = find_worlds(db.clone(), ids) .await .expect("worlds could not be retrieved"); - let mut updated_worlds: Vec = Vec::with_capacity(q as usize); + let mut updated_worlds: Vec = Vec::with_capacity(q); for mut world in worlds { let random_number = (rng.gen::() % 10_000 + 1) as i32; @@ -125,28 +116,13 @@ async fn fortunes(DatabaseConnection(db): DatabaseConnection) -> impl IntoRespon fn main() { dotenv().ok(); - - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - for _ in 1..num_cpus::get() { - std::thread::spawn(move || { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(serve()); - }); - } - rt.block_on(serve()); + server::start_tokio(serve_app) } -async fn serve() { - let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_MONGODB_URL"); - let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE"); - let min_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MIN_POOL_SIZE"); +async fn serve_app() { + let database_url: String = get_env("MONGODB_URL"); + let max_pool_size: u32 = get_env("MONGODB_MAX_POOL_SIZE"); + let min_pool_size: u32 = get_env("MONGODB_MIN_POOL_SIZE"); let mut client_options = ClientOptions::parse(database_url).await.unwrap(); @@ -168,21 +144,13 @@ async fn serve() { let client = Client::with_options(client_options).unwrap(); let database = client.database("hello_world"); - let server_header_value = HeaderValue::from_static("Axum"); let app = Router::new() .route("/fortunes", get(fortunes)) .route("/db", get(db)) .route("/queries", get(queries)) .route("/updates", get(updates)) - .with_state(database) - .layer(SetResponseHeaderLayer::if_not_present( - header::SERVER, - server_header_value, - )); - - server::builder() - .serve(app.into_make_service()) - .await - .unwrap(); + .with_state(database); + + server::serve(app, Some(8000)).await } diff --git a/frameworks/Rust/axum/src/main_mongo_raw.rs b/frameworks/Rust/axum/src/main_mongo_raw.rs index 72db28af280..0d2735ad3bb 100644 --- a/frameworks/Rust/axum/src/main_mongo_raw.rs +++ b/frameworks/Rust/axum/src/main_mongo_raw.rs @@ -1,38 +1,38 @@ +mod common; +mod mongo_raw; +mod server; + +use common::{models::World, random_id, random_ids}; +use mongo_raw::database::{ + find_world_by_id, find_worlds, update_worlds, DatabaseConnection, +}; + +use common::{ + get_env, + utils::{parse_params, Params}, +}; use std::time::Duration; use axum::{ - extract::Query, - http::{header, HeaderValue, StatusCode}, - response::IntoResponse, - routing::get, - Json, Router, + extract::Query, http::StatusCode, response::IntoResponse, routing::get, Router, }; + +#[cfg(not(feature = "simd-json"))] +use axum::Json; +#[cfg(feature = "simd-json")] +use common::simd_json::Json; + use dotenv::dotenv; use mongodb::{ options::{ClientOptions, Compressor}, Client, }; use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng}; -use tower_http::set_header::SetResponseHeaderLayer; - -mod database_mongo_raw; -mod models_common; -mod models_mongo; -mod server; -mod utils; - -use self::{ - database_mongo_raw::{ - find_world_by_id, find_worlds, update_worlds, DatabaseConnection, - }, - models_mongo::World, - utils::{get_environment_variable, parse_params, Params}, -}; async fn db(DatabaseConnection(db): DatabaseConnection) -> impl IntoResponse { let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let random_id = (rng.gen::() % 10_000 + 1) as i32; + let random_id = random_id(&mut rng); let world = find_world_by_id(db, random_id) .await @@ -48,13 +48,7 @@ async fn queries( let q = parse_params(params); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let mut ids: Vec = Vec::with_capacity(q as usize); - - for _ in 0..q { - let random_id = (rng.gen::() % 10_000 + 1) as i32; - - ids.push(random_id); - } + let ids = random_ids(&mut rng, q); let worlds = find_worlds(db, ids).await; let results = worlds.expect("worlds could not be retrieved"); @@ -69,18 +63,12 @@ async fn updates( let q = parse_params(params); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let mut ids: Vec = Vec::with_capacity(q as usize); - - for _ in 0..q { - let random_id = (rng.gen::() % 10_000 + 1) as i32; - - ids.push(random_id); - } + let ids = random_ids(&mut rng, q); let worlds = find_worlds(db.clone(), ids) .await .expect("worlds could not be retrieved"); - let mut updated_worlds: Vec = Vec::with_capacity(q as usize); + let mut updated_worlds: Vec = Vec::with_capacity(q); for mut world in worlds { let random_number = (rng.gen::() % 10_000 + 1) as i32; @@ -98,28 +86,13 @@ async fn updates( fn main() { dotenv().ok(); - - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - for _ in 1..num_cpus::get() { - std::thread::spawn(move || { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(serve()); - }); - } - rt.block_on(serve()); + server::start_tokio(serve_app) } -async fn serve() { - let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_MONGODB_URL"); - let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE"); - let min_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MIN_POOL_SIZE"); +async fn serve_app() { + let database_url: String = get_env("MONGODB_URL"); + let max_pool_size: u32 = get_env("MONGODB_MAX_POOL_SIZE"); + let min_pool_size: u32 = get_env("MONGODB_MIN_POOL_SIZE"); let mut client_options = ClientOptions::parse(database_url).await.unwrap(); @@ -141,20 +114,12 @@ async fn serve() { let client = Client::with_options(client_options).unwrap(); let database = client.database("hello_world"); - let server_header_value = HeaderValue::from_static("Axum"); let app = Router::new() .route("/db", get(db)) .route("/queries", get(queries)) .route("/updates", get(updates)) - .with_state(database) - .layer(SetResponseHeaderLayer::if_not_present( - header::SERVER, - server_header_value, - )); - - server::builder() - .serve(app.into_make_service()) - .await - .unwrap(); + .with_state(database); + + server::serve(app, Some(8000)).await } diff --git a/frameworks/Rust/axum/src/main_pg.rs b/frameworks/Rust/axum/src/main_pg.rs index 3b4748dfc39..be691f9720c 100644 --- a/frameworks/Rust/axum/src/main_pg.rs +++ b/frameworks/Rust/axum/src/main_pg.rs @@ -1,25 +1,26 @@ +mod common; +mod pg; + use axum::{ - extract::Query, - http::{header, HeaderValue, StatusCode}, - response::IntoResponse, - routing::get, - Json, Router, + extract::Query, http::StatusCode, response::IntoResponse, routing::get, Router, }; use dotenv::dotenv; -use tower_http::set_header::SetResponseHeaderLayer; +use rand::{rngs::SmallRng, thread_rng, SeedableRng}; use yarte::Template; -mod database_pg; -mod models_common; -mod models_pg; +#[cfg(not(feature = "simd-json"))] +use axum::Json; +#[cfg(feature = "simd-json")] +use common::simd_json::Json; + mod server; -mod utils; -use self::{ - database_pg::{DatabaseConnection, PgConnection}, - models_pg::Fortune, - utils::{get_environment_variable, parse_params, Params, Utf8Html}, +use common::{ + get_env, random_id, + utils::{parse_params, Params, Utf8Html}, }; +use pg::database::{DatabaseConnection, PgConnection}; +use pg::models::Fortune; #[derive(Template)] #[template(path = "fortunes.html.hbs")] @@ -28,7 +29,12 @@ pub struct FortunesTemplate<'a> { } async fn db(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse { - let world = conn.get_world().await.expect("error loading world"); + let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + + let world = conn + .fetch_world_by_id(random_id(&mut rng)) + .await + .expect("error loading world"); (StatusCode::OK, Json(world)) } @@ -40,7 +46,7 @@ async fn queries( let q = parse_params(params); let results = conn - .get_worlds(q as usize) + .fetch_random_worlds(q) .await .expect("error loading worlds"); @@ -48,8 +54,10 @@ async fn queries( } async fn fortunes(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse { - let fortunes: Vec = - conn.tell_fortune().await.expect("error loading fortunes"); + let fortunes: Vec = conn + .fetch_all_fortunes() + .await + .expect("error loading fortunes"); Utf8Html( FortunesTemplate { @@ -65,52 +73,28 @@ async fn updates( Query(params): Query, ) -> impl IntoResponse { let q = parse_params(params); + let worlds = conn.update_worlds(q).await.expect("error updating worlds"); - let results = conn.update(q as u16).await.expect("error updating worlds"); - - (StatusCode::OK, Json(results)) + (StatusCode::OK, Json(worlds)) } fn main() { dotenv().ok(); - - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - for _ in 1..num_cpus::get() { - std::thread::spawn(move || { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(serve()); - }); - } - rt.block_on(serve()); + server::start_tokio(serve_app) } -async fn serve() { - let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_DATABASE_URL"); +async fn serve_app() { + let database_url: String = get_env("POSTGRES_URL"); - // setup connection pool + // Create shared database connection let pg_connection = PgConnection::connect(database_url).await; - let server_header_value = HeaderValue::from_static("Axum"); - let router = Router::new() + let app = Router::new() .route("/fortunes", get(fortunes)) .route("/db", get(db)) .route("/queries", get(queries)) .route("/updates", get(updates)) - .with_state(pg_connection) - .layer(SetResponseHeaderLayer::if_not_present( - header::SERVER, - server_header_value, - )); - - server::builder() - .serve(router.into_make_service()) - .await - .unwrap(); + .with_state(pg_connection); + + server::serve_hyper(app, Some(8000)).await } diff --git a/frameworks/Rust/axum/src/main_pg_pool.rs b/frameworks/Rust/axum/src/main_pg_pool.rs index 4c767b4a06c..d4b7754cba8 100644 --- a/frameworks/Rust/axum/src/main_pg_pool.rs +++ b/frameworks/Rust/axum/src/main_pg_pool.rs @@ -1,31 +1,31 @@ +mod common; +mod pg_pool; + use axum::{ - extract::Query, - http::{header, HeaderValue, StatusCode}, - response::IntoResponse, - routing::get, - Json, Router, + extract::Query, http::StatusCode, response::IntoResponse, routing::get, Router, }; + +#[cfg(not(feature = "simd-json"))] +use axum::Json; +#[cfg(feature = "simd-json")] +use common::simd_json::Json; + +use common::{random_ids, SELECT_ALL_FORTUNES, SELECT_WORLD_BY_ID, UPDATE_WORLDS}; use dotenv::dotenv; use futures_util::{stream::FuturesUnordered, TryStreamExt}; -use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng}; -use tower_http::set_header::SetResponseHeaderLayer; +use rand::{rngs::SmallRng, thread_rng, SeedableRng}; use yarte::Template; -mod database_pg_pool; -mod models_common; -mod models_pg_pool; mod server; -mod utils; - -use self::{ - database_pg_pool::{ - create_pool, fetch_all_fortunes, fetch_world_by_id, - prepare_fetch_all_fortunes_statement, prepare_fetch_world_by_id_statement, - prepare_update_world_by_id_statement, update_world, DatabaseClient, PgError, - }, - models_pg_pool::{Fortune, World}, - utils::{get_environment_variable, parse_params, random_number, Params, Utf8Html}, + +use common::{ + get_env, random_id, + utils::{parse_params, Params, Utf8Html}, }; +use pg_pool::database::{ + create_pool, fetch_all_fortunes, fetch_world_by_id, DatabaseClient, PgError, +}; +use pg_pool::models::{Fortune, World}; #[derive(Template)] #[template(path = "fortunes.html.hbs")] @@ -35,11 +35,10 @@ pub struct FortunesTemplate<'a> { async fn db(DatabaseClient(client): DatabaseClient) -> impl IntoResponse { let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + let random_id = random_id(&mut rng); - let random_id = (rng.gen::() % 10_000 + 1) as i32; - - let select = prepare_fetch_world_by_id_statement(&client).await; - let world = fetch_world_by_id(&client, random_id, &select) + let select = &client.prepare_cached(SELECT_WORLD_BY_ID).await.unwrap(); + let world = fetch_world_by_id(&client, random_id, select) .await .expect("could not fetch world"); @@ -53,15 +52,11 @@ async fn queries( let q = parse_params(params); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - - let select = prepare_fetch_world_by_id_statement(&client).await; - + let select = &client.prepare_cached(SELECT_WORLD_BY_ID).await.unwrap(); let future_worlds = FuturesUnordered::new(); - for _ in 0..q { - let w_id = (rng.gen::() % 10_000 + 1) as i32; - - future_worlds.push(fetch_world_by_id(&client, w_id, &select)); + for id in random_ids(&mut rng, q) { + future_worlds.push(fetch_world_by_id(&client, id, select)); } let worlds: Result, PgError> = future_worlds.try_collect().await; @@ -71,9 +66,9 @@ async fn queries( } async fn fortunes(DatabaseClient(client): DatabaseClient) -> impl IntoResponse { - let select = prepare_fetch_all_fortunes_statement(&client).await; + let select = &client.prepare_cached(SELECT_ALL_FORTUNES).await.unwrap(); - let mut fortunes = fetch_all_fortunes(client, &select) + let mut fortunes = fetch_all_fortunes(client, select) .await .expect("could not fetch fortunes"); @@ -100,66 +95,49 @@ async fn updates( let q = parse_params(params); let mut rng = SmallRng::from_entropy(); + let select = &client.prepare_cached(SELECT_WORLD_BY_ID).await.unwrap(); + let update = &client.prepare_cached(UPDATE_WORLDS).await.unwrap(); - let select = prepare_fetch_world_by_id_statement(&client).await; - + // Select the random worlds. let future_worlds = FuturesUnordered::new(); - - for _ in 0..q { - let query_id = random_number(&mut rng); - - future_worlds.push(fetch_world_by_id(&client, query_id, &select)); + for id in random_ids(&mut rng, q) { + future_worlds.push(fetch_world_by_id(&client, id, select)); } - - let worlds: Result, PgError> = future_worlds.try_collect().await; - let results = worlds.expect("worlds could not be retrieved"); - - let update = prepare_update_world_by_id_statement(&client).await; - - let future_world_updates = FuturesUnordered::new(); - - for w in &results { - let random_id = random_number(&mut rng); - let w_id = w.id; - - future_world_updates.push(update_world(&client, &update, random_id, w_id)); - } - - let world_updates: Result, PgError> = - future_world_updates.try_collect().await; - world_updates.expect("updates could not be executed"); - - (StatusCode::OK, Json(results)) + let worlds: Vec = future_worlds.try_collect().await.unwrap(); + + let mut ids = Vec::with_capacity(q); + let mut nids = Vec::with_capacity(q); + 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(); + + // Update the random worlds in the database. + client.execute(update, &[&ids, &nids]).await.unwrap(); + + (StatusCode::OK, Json(worlds)) } #[tokio::main] async fn main() { dotenv().ok(); - serve().await; -} - -async fn serve() { - let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_DATABASE_URL"); - let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE"); + let database_url: String = get_env("POSTGRES_URL"); + let max_pool_size: u32 = get_env("POSTGRES_MAX_POOL_SIZE"); - // setup Client pool let pool = create_pool(database_url, max_pool_size).await; - let server_header_value = HeaderValue::from_static("Axum"); - let router = Router::new() + let app = Router::new() .route("/fortunes", get(fortunes)) .route("/db", get(db)) .route("/queries", get(queries)) .route("/updates", get(updates)) - .with_state(pool) - .layer(SetResponseHeaderLayer::if_not_present( - header::SERVER, - server_header_value, - )); - - server::builder() - .serve(router.into_make_service()) - .await - .unwrap(); + .with_state(pool); + + server::serve_hyper(app, Some(8000)).await } diff --git a/frameworks/Rust/axum/src/main_sqlx.rs b/frameworks/Rust/axum/src/main_sqlx.rs index c773233fcfa..b3a7937841b 100644 --- a/frameworks/Rust/axum/src/main_sqlx.rs +++ b/frameworks/Rust/axum/src/main_sqlx.rs @@ -1,27 +1,35 @@ +mod common; +mod sqlx; + +use std::sync::Arc; + +use ::sqlx::PgPool; use axum::{ - http::{header, HeaderValue, StatusCode}, + extract::{Query, State}, + http::StatusCode, response::IntoResponse, routing::get, - Json, Router, + Router, }; use dotenv::dotenv; -use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng}; -use sqlx::PgPool; -use tower_http::set_header::SetResponseHeaderLayer; +use moka::future::Cache; +use rand::{rngs::SmallRng, thread_rng, SeedableRng}; +use sqlx::models::World; use yarte::Template; -mod database_sqlx; -mod models_common; -mod models_sqlx; +#[cfg(not(feature = "simd-json"))] +use axum::Json; +#[cfg(feature = "simd-json")] +use common::simd_json::Json; + mod server; -mod utils; -use self::{ - database_sqlx::{create_pool, fetch_fortunes, fetch_world, DatabaseConnection}, - models_sqlx::{Fortune, World}, - utils::get_environment_variable, - utils::Utf8Html, +use common::{ + get_env, random_id, random_ids, + utils::{parse_params, Params, Utf8Html}, }; +use sqlx::database::create_pool; +use sqlx::models::Fortune; #[derive(Template)] #[template(path = "fortunes.html.hbs")] @@ -29,22 +37,44 @@ pub struct FortunesTemplate<'a> { pub fortunes: &'a Vec, } -async fn db(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse { +async fn db(State(AppState { db, .. }): State) -> impl IntoResponse { let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let random_id = (rng.gen::() % 10_000 + 1) as i32; - - let world = fetch_world(conn, random_id) + let world: World = ::sqlx::query_as(common::SELECT_WORLD_BY_ID) + .bind(random_id(&mut rng)) + .fetch_one(&mut *db.acquire().await.unwrap()) .await - .expect("could not fetch world"); + .expect("error loading world"); (StatusCode::OK, Json(world)) } -async fn fortunes(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse { - let mut fortunes = fetch_fortunes(conn) +async fn queries( + State(AppState { db, .. }): State, + Query(params): Query, +) -> impl IntoResponse { + let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + let count = parse_params(params); + let ids = random_ids(&mut rng, count); + let mut worlds: Vec = Vec::with_capacity(count); + + for id in &ids { + let world: World = ::sqlx::query_as(common::SELECT_WORLD_BY_ID) + .bind(id) + .fetch_one(&mut *db.acquire().await.unwrap()) + .await + .expect("error loading world"); + worlds.push(world); + } + + (StatusCode::OK, Json(worlds)) +} + +async fn fortunes(State(AppState { db, .. }): State) -> impl IntoResponse { + let mut fortunes: Vec = ::sqlx::query_as(common::SELECT_ALL_FORTUNES) + .fetch_all(&mut *db.acquire().await.unwrap()) .await - .expect("could not fetch fortunes"); + .expect("error loading Fortunes"); fortunes.push(Fortune { id: 0, @@ -62,34 +92,65 @@ async fn fortunes(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResp ) } -#[tokio::main] -async fn main() { - dotenv().ok(); - - let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_DATABASE_URL"); - let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE"); - let min_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MIN_POOL_SIZE"); +async fn cache( + State(AppState { cache, .. }): State, + Query(params): Query, +) -> 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); - // setup connection pool - let pool = create_pool(database_url, max_pool_size, min_pool_size).await; + for id in random_ids(&mut rng, count) { + worlds.push(cache.get(&id).await); + } - let app = router(pool).await; + (StatusCode::OK, Json(worlds)) +} - server::builder() - .serve(app.into_make_service()) +/// Pre-load the cache with all worlds. +async fn preload_cache(AppState { db, cache }: &AppState) { + let worlds: Vec = ::sqlx::query_as(common::SELECT_ALL_CACHED_WORLDS) + .fetch_all(&mut *db.acquire().await.unwrap()) .await - .unwrap(); + .expect("error loading worlds"); + + for world in worlds { + cache.insert(world.id, Arc::new(world)).await; + } } -async fn router(pool: PgPool) -> Router { - let server_header_value = HeaderValue::from_static("Axum"); +/// Application state +#[derive(Clone)] +struct AppState { + db: PgPool, + cache: Cache>, +} - Router::new() +#[tokio::main] +async fn main() { + dotenv().ok(); + + let database_url: String = get_env("POSTGRES_URL"); + let max_pool_size: u32 = get_env("POSTGRES_MAX_POOL_SIZE"); + let min_pool_size: u32 = get_env("POSTGRES_MIN_POOL_SIZE"); + + let state = AppState { + db: create_pool(database_url, max_pool_size, min_pool_size).await, + cache: Cache::builder() + .initial_capacity(10000) + .max_capacity(10000) + .build() + }; + + // Prime the cache with CachedWorld objects + preload_cache(&state).await; + + let app = Router::new() .route("/fortunes", get(fortunes)) .route("/db", get(db)) - .with_state(pool) - .layer(SetResponseHeaderLayer::if_not_present( - header::SERVER, - server_header_value, - )) + .route("/queries", get(queries)) + .route("/cached-queries", get(cache)) + .with_state(state); + + server::serve_hyper(app, Some(8000)).await } diff --git a/frameworks/Rust/axum/src/models_common.rs b/frameworks/Rust/axum/src/models_common.rs deleted file mode 100644 index 9aed68955a4..00000000000 --- a/frameworks/Rust/axum/src/models_common.rs +++ /dev/null @@ -1,6 +0,0 @@ -use serde::Serialize; - -#[derive(Serialize)] -pub struct Message { - pub message: &'static str, -} diff --git a/frameworks/Rust/axum/src/database_mongo.rs b/frameworks/Rust/axum/src/mongo/database.rs similarity index 97% rename from frameworks/Rust/axum/src/database_mongo.rs rename to frameworks/Rust/axum/src/mongo/database.rs index dcb9b1bebb9..28c55d25c01 100644 --- a/frameworks/Rust/axum/src/database_mongo.rs +++ b/frameworks/Rust/axum/src/mongo/database.rs @@ -4,7 +4,7 @@ use axum::{async_trait, extract::FromRequestParts, http::request::Parts}; use futures_util::{stream::FuturesUnordered, StreamExt, TryStreamExt}; use mongodb::{bson::doc, Database}; -use crate::{Fortune, World}; +use crate::common::models::{Fortune, World}; pub struct DatabaseConnection(pub Database); @@ -21,6 +21,7 @@ impl FromRequestParts for DatabaseConnection { } #[derive(Debug)] +#[allow(dead_code)] pub enum MongoError { Io(io::Error), Mongo(mongodb::error::Error), diff --git a/frameworks/Rust/axum/src/mongo/mod.rs b/frameworks/Rust/axum/src/mongo/mod.rs new file mode 100644 index 00000000000..8fd0a6be869 --- /dev/null +++ b/frameworks/Rust/axum/src/mongo/mod.rs @@ -0,0 +1 @@ +pub mod database; diff --git a/frameworks/Rust/axum/src/database_mongo_raw.rs b/frameworks/Rust/axum/src/mongo_raw/database.rs similarity index 97% rename from frameworks/Rust/axum/src/database_mongo_raw.rs rename to frameworks/Rust/axum/src/mongo_raw/database.rs index 2fa02c07ec4..638c590c9fa 100644 --- a/frameworks/Rust/axum/src/database_mongo_raw.rs +++ b/frameworks/Rust/axum/src/mongo_raw/database.rs @@ -7,7 +7,7 @@ use mongodb::{ Database, }; -use crate::World; +use crate::common::models::World; pub struct DatabaseConnection(pub Database); @@ -24,6 +24,7 @@ impl FromRequestParts for DatabaseConnection { } #[derive(Debug)] +#[allow(dead_code)] pub enum MongoError { Io(io::Error), Mongo(mongodb::error::Error), diff --git a/frameworks/Rust/axum/src/mongo_raw/mod.rs b/frameworks/Rust/axum/src/mongo_raw/mod.rs new file mode 100644 index 00000000000..8fd0a6be869 --- /dev/null +++ b/frameworks/Rust/axum/src/mongo_raw/mod.rs @@ -0,0 +1 @@ +pub mod database; diff --git a/frameworks/Rust/axum/src/pg/database.rs b/frameworks/Rust/axum/src/pg/database.rs new file mode 100644 index 00000000000..b9cff086c5a --- /dev/null +++ b/frameworks/Rust/axum/src/pg/database.rs @@ -0,0 +1,152 @@ +use std::{convert::Infallible, io, sync::Arc}; + +use axum::{async_trait, extract::FromRequestParts, http::request::Parts}; +use futures::{stream::futures_unordered::FuturesUnordered, StreamExt, TryStreamExt}; +use rand::{rngs::SmallRng, thread_rng, SeedableRng}; +use tokio::pin; +use tokio_postgres::{connect, Client, NoTls, Statement}; + +use crate::common::{self, random_id, random_ids}; + +use super::models::{Fortune, World}; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum PgError { + Io(io::Error), + Pg(tokio_postgres::Error), +} + +impl From for PgError { + fn from(err: io::Error) -> Self { + PgError::Io(err) + } +} + +impl From for PgError { + fn from(err: tokio_postgres::Error) -> Self { + PgError::Pg(err) + } +} + +/// Postgres interface +pub struct PgConnection { + client: Client, + fortune: Statement, + world: Statement, + updates: Statement, +} + +impl PgConnection { + pub async fn connect(db_url: String) -> Arc { + let (cl, conn) = connect(&db_url, NoTls) + .await + .expect("cannot connect to postgresql."); + + // Spawn connection + tokio::spawn(async move { + if let Err(error) = conn.await { + eprintln!("database connection error: {error}"); + } + }); + + // Prepare statements for the connection. + let fortune = cl.prepare(common::SELECT_ALL_FORTUNES).await.unwrap(); + let world = cl.prepare(common::SELECT_WORLD_BY_ID).await.unwrap(); + let updates = cl.prepare(common::UPDATE_WORLDS).await.unwrap(); + + Arc::new(PgConnection { + client: cl, + fortune, + world, + updates, + }) + } +} + +impl PgConnection { + pub async fn fetch_world_by_id(&self, id: i32) -> Result { + self.client + .query_one(&self.world, &[&id]) + .await + .map(|row| { + Ok(World { + id: row.get(0), + randomnumber: row.get(1), + }) + })? + } + + pub async fn fetch_random_worlds(&self, num: usize) -> Result, PgError> { + let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + + let futures = FuturesUnordered::new(); + + for id in random_ids(&mut rng, num) { + futures.push(self.fetch_world_by_id(id)); + } + + futures.try_collect().await + } + + pub async fn update_worlds(&self, num: usize) -> Result, PgError> { + 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); + + 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 + .execute(&self.updates, &[&ids, &nids]) + .await + .unwrap(); + + Ok(worlds) + } + + pub async fn fetch_all_fortunes(&self) -> Result, PgError> { + let mut fortunes = vec![Fortune { + id: 0, + message: "Additional fortune added at request time.".parse().unwrap(), + }]; + + let rows = self + .client + .query_raw::<_, _, &[i32; 0]>(&self.fortune, &[]) + .await?; + + pin!(rows); + + while let Some(row) = rows.next().await.transpose()? { + fortunes.push(Fortune { + id: row.get(0), + message: row.get(1), + }); + } + + fortunes.sort_by(|it, next| it.message.cmp(&next.message)); + Ok(fortunes) + } +} + +pub struct DatabaseConnection(pub Arc); + +#[async_trait] +impl FromRequestParts> for DatabaseConnection { + type Rejection = Infallible; + + async fn from_request_parts( + _parts: &mut Parts, + pg_connection: &Arc, + ) -> Result { + Ok(Self(pg_connection.clone())) + } +} diff --git a/frameworks/Rust/axum/src/pg/mod.rs b/frameworks/Rust/axum/src/pg/mod.rs new file mode 100644 index 00000000000..c51e02d23d3 --- /dev/null +++ b/frameworks/Rust/axum/src/pg/mod.rs @@ -0,0 +1,2 @@ +pub mod database; +pub mod models; diff --git a/frameworks/Rust/axum/src/models_pg.rs b/frameworks/Rust/axum/src/pg/models.rs similarity index 100% rename from frameworks/Rust/axum/src/models_pg.rs rename to frameworks/Rust/axum/src/pg/models.rs diff --git a/frameworks/Rust/axum/src/database_pg_pool.rs b/frameworks/Rust/axum/src/pg_pool/database.rs similarity index 60% rename from frameworks/Rust/axum/src/database_pg_pool.rs rename to frameworks/Rust/axum/src/pg_pool/database.rs index f86c002845a..e1a43f34f23 100644 --- a/frameworks/Rust/axum/src/database_pg_pool.rs +++ b/frameworks/Rust/axum/src/pg_pool/database.rs @@ -1,17 +1,22 @@ use std::io; +use crate::{ + common::utils::internal_error, + pg_pool::models::{Fortune, World}, +}; use axum::{ async_trait, extract::FromRequestParts, http::{request::Parts, StatusCode}, }; use deadpool_postgres::{Client, Manager, ManagerConfig, RecyclingMethod}; +use futures_util::StreamExt; +use tokio::pin; use tokio_pg_mapper::FromTokioPostgresRow; use tokio_postgres::{NoTls, Row, Statement}; -use crate::{utils::internal_error, Fortune, World}; - #[derive(Debug)] +#[allow(dead_code)] pub enum PgError { Io(io::Error), Pg(tokio_postgres::Error), @@ -66,57 +71,26 @@ impl FromRequestParts for DatabaseClient { pub async fn fetch_world_by_id( client: &Client, - number: i32, + id: i32, select: &Statement, ) -> Result { - let row: Row = client.query_one(select, &[&number]).await.unwrap(); + let row: Row = client.query_one(select, &[&id]).await.unwrap(); Ok(World::from_row(row).unwrap()) } -pub async fn update_world( - client: &Client, - update: &Statement, - random_id: i32, - w_id: i32, -) -> Result { - let rows_modified: u64 = client.execute(update, &[&random_id, &w_id]).await.unwrap(); - - Ok(rows_modified) -} - pub async fn fetch_all_fortunes( client: Client, select: &Statement, ) -> Result, PgError> { - let rows: Vec = client.query(select, &[]).await.unwrap(); - - let mut fortunes: Vec = Vec::with_capacity(rows.capacity()); + let mut fortunes: Vec = Vec::new(); + let rows = client.query_raw::<_, _, &[i32; 0]>(select, &[]).await?; + pin!(rows); - for row in rows { - fortunes.push(Fortune::from_row(row).unwrap()); + while let Some(row) = rows.next().await.transpose()? { + fortunes + .push(Fortune::from_row(row).expect("could not convert row to fortune.")); } Ok(fortunes) } - -pub async fn prepare_fetch_all_fortunes_statement(client: &Client) -> Statement { - client - .prepare_cached("SELECT * FROM Fortune") - .await - .unwrap() -} - -pub async fn prepare_fetch_world_by_id_statement(client: &Client) -> Statement { - client - .prepare_cached("SELECT id, randomnumber FROM World WHERE id = $1") - .await - .unwrap() -} - -pub async fn prepare_update_world_by_id_statement(client: &Client) -> Statement { - client - .prepare_cached("UPDATE World SET randomnumber = $1 WHERE id = $2") - .await - .unwrap() -} diff --git a/frameworks/Rust/axum/src/pg_pool/mod.rs b/frameworks/Rust/axum/src/pg_pool/mod.rs new file mode 100644 index 00000000000..c51e02d23d3 --- /dev/null +++ b/frameworks/Rust/axum/src/pg_pool/mod.rs @@ -0,0 +1,2 @@ +pub mod database; +pub mod models; diff --git a/frameworks/Rust/axum/src/models_pg_pool.rs b/frameworks/Rust/axum/src/pg_pool/models.rs similarity index 100% rename from frameworks/Rust/axum/src/models_pg_pool.rs rename to frameworks/Rust/axum/src/pg_pool/models.rs diff --git a/frameworks/Rust/axum/src/server.rs b/frameworks/Rust/axum/src/server.rs index 29e165f6f5c..6d861277c10 100644 --- a/frameworks/Rust/axum/src/server.rs +++ b/frameworks/Rust/axum/src/server.rs @@ -1,37 +1,130 @@ use std::{ + future::Future, io, - net::{Ipv4Addr, SocketAddr}, + net::{Ipv4Addr, SocketAddr, TcpListener}, }; -use hyper::server::conn::AddrIncoming; -use tokio::net::{TcpListener, TcpSocket}; - -pub fn builder() -> hyper::server::Builder { - let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8000)); - let listener = reuse_listener(addr).expect("couldn't bind to addr"); - let incoming = AddrIncoming::from_listener(listener).unwrap(); +use axum::{ + http::{header, HeaderValue}, + Router, +}; - println!("Started axum server at 8000"); +use hyper::body::Incoming; +use hyper::Request; +use hyper_util::rt::{TokioExecutor, TokioIo}; +use tower::Service; +use tower_http::set_header::SetResponseHeaderLayer; - axum::Server::builder(incoming) - .http1_only(true) - .tcp_nodelay(true) -} +use socket2::{Domain, Socket, Type}; -fn reuse_listener(addr: SocketAddr) -> io::Result { +/// Reuse an existing listener, ensuring that the socket `backlog`` +/// is set to enable a higher number of pending connections. +fn set_socket_options(addr: SocketAddr) -> io::Result { let socket = match addr { - SocketAddr::V4(_) => TcpSocket::new_v4()?, - SocketAddr::V6(_) => TcpSocket::new_v6()?, + SocketAddr::V4(_) => Socket::new(Domain::IPV4, Type::STREAM, None)?, + SocketAddr::V6(_) => Socket::new(Domain::IPV6, Type::STREAM, None)?, }; - #[cfg(unix)] - { - if let Err(e) = socket.set_reuseport(true) { - eprintln!("error setting SO_REUSEPORT: {e}"); - } + socket.set_reuse_port(true)?; + socket.set_reuse_address(true)?; + socket.set_nonblocking(true)?; + socket.set_nodelay(true)?; + socket.bind(&addr.into())?; + socket.listen(4096)?; + + let listener: TcpListener = socket.into(); + tokio::net::TcpListener::from_std(listener) +} + +/// Build an Axum server with consistent configuration, using the high-level API exposed +/// by Axum 0.7. This is intended for convenience and intentionally does not provide much +/// customisability. +#[allow(dead_code)] +pub async fn serve(app: Router<()>, port: Option) { + let port = port.unwrap_or(8000); + let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, port)); + let listener = set_socket_options(addr).expect("couldn't bind to address"); + println!("started axum server on port {port}."); + + let server_header_value = HeaderValue::from_static("Axum"); + let app = app.layer(SetResponseHeaderLayer::overriding( + header::SERVER, + server_header_value, + )); + + axum::serve(listener, app.into_make_service()) + .await + .unwrap(); +} + +/// Build an Axum server using the lower-level Hyper APIs for more +/// configurability. This has a few optimisations, including: +/// * Serving HTTP/1 only. +/// * Disabling connection upgrades (websockets are not needed). +/// * Setting TCP_NODELAY on the input stream. +/// * Aggregating flushes to better support pipelined responses. +/// +/// See for more details: +/// * https://github.com/tokio-rs/axum/blob/1ac617a1b540e8523347f5ee889d65cad9a45ec4/examples/serve-with-hyper/src/main.rs +#[allow(dead_code)] +pub async fn serve_hyper(app: Router<()>, port: Option) { + let port = port.unwrap_or(8000); + let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, port)); + let listener = set_socket_options(addr).expect("couldn't bind to address"); + println!("started axum server on port {port}."); + + let server_header_value = HeaderValue::from_static("Axum"); + let app = app.layer(SetResponseHeaderLayer::overriding( + header::SERVER, + server_header_value, + )); + + // Continuously accept new connections. + loop { + let (socket, _remote_addr) = listener.accept().await.unwrap(); + socket + .set_nodelay(true) + .expect("could not set TCP_NODELAY!"); + + let tower_service = app.clone(); + tokio::spawn(async move { + let socket = TokioIo::new(socket); + + let hyper_service = + hyper::service::service_fn(move |request: Request| { + tower_service.clone().call(request) + }); + + if (hyper_util::server::conn::auto::Builder::new(TokioExecutor::new()) + .http1() + .pipeline_flush(true) + .serve_connection(socket, hyper_service) + .await) + .is_err() + {} + }); } +} - socket.set_reuseaddr(true)?; - socket.bind(addr)?; - socket.listen(1024) +/// Start a single-threaded tokio runtime on multiple threads. +#[allow(dead_code)] +pub fn start_tokio(f: fn() -> Fut) +where + Fut: Future + 'static, +{ + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + for _ in 1..num_cpus::get() { + std::thread::spawn(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(f()); + }); + } + rt.block_on(f()); } diff --git a/frameworks/Rust/axum/src/sqlx/database.rs b/frameworks/Rust/axum/src/sqlx/database.rs new file mode 100644 index 00000000000..1d824144696 --- /dev/null +++ b/frameworks/Rust/axum/src/sqlx/database.rs @@ -0,0 +1,36 @@ +use std::io; + +use sqlx::{postgres::PgPoolOptions, PgPool}; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum PgError { + Io(io::Error), + Pg(sqlx::Error), +} + +impl From for PgError { + fn from(err: io::Error) -> Self { + PgError::Io(err) + } +} + +impl From for PgError { + fn from(err: sqlx::Error) -> Self { + PgError::Pg(err) + } +} + +pub async fn create_pool( + database_url: String, + max_pool_size: u32, + min_pool_size: u32, +) -> PgPool { + PgPoolOptions::new() + .max_connections(max_pool_size) + .min_connections(min_pool_size) + .test_before_acquire(false) + .connect(&database_url) + .await + .unwrap() +} diff --git a/frameworks/Rust/axum/src/sqlx/mod.rs b/frameworks/Rust/axum/src/sqlx/mod.rs new file mode 100644 index 00000000000..c51e02d23d3 --- /dev/null +++ b/frameworks/Rust/axum/src/sqlx/mod.rs @@ -0,0 +1,2 @@ +pub mod database; +pub mod models; diff --git a/frameworks/Rust/axum/src/models_sqlx.rs b/frameworks/Rust/axum/src/sqlx/models.rs similarity index 100% rename from frameworks/Rust/axum/src/models_sqlx.rs rename to frameworks/Rust/axum/src/sqlx/models.rs diff --git a/frameworks/Rust/may-minihttp/Cargo.toml b/frameworks/Rust/may-minihttp/Cargo.toml index 019aaf898a2..9fc7760ff8e 100644 --- a/frameworks/Rust/may-minihttp/Cargo.toml +++ b/frameworks/Rust/may-minihttp/Cargo.toml @@ -19,7 +19,7 @@ buf-min = { version = "0.7", features = ["bytes"] } may = { version = "0.3", default-features = false } may_minihttp = { version = "0.1", default-features = false } -may_postgres = { git = "https://github.com/Xudong-Huang/may_postgres.git", rev = "5ea3fb9", default-features = false } +may_postgres = { git = "https://github.com/Xudong-Huang/may_postgres.git", rev = "917ed78", default-features = false } [profile.release] opt-level = 3 diff --git a/frameworks/Rust/may-minihttp/may-minihttp.dockerfile b/frameworks/Rust/may-minihttp/may-minihttp.dockerfile index 0ddda28e3eb..160e563ac2c 100644 --- a/frameworks/Rust/may-minihttp/may-minihttp.dockerfile +++ b/frameworks/Rust/may-minihttp/may-minihttp.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78 +FROM rust:1.79 RUN apt-get update -yqq && apt-get install -yqq cmake g++ diff --git a/frameworks/Rust/may-minihttp/src/main.rs b/frameworks/Rust/may-minihttp/src/main.rs index f6a817031c3..33b410b1c0f 100644 --- a/frameworks/Rust/may-minihttp/src/main.rs +++ b/frameworks/Rust/may-minihttp/src/main.rs @@ -49,7 +49,7 @@ struct PgConnectionPool { impl PgConnectionPool { fn new(db_url: &'static str, size: usize) -> PgConnectionPool { let clients = (0..size) - .map(|_| std::thread::spawn(move || PgConnection::new(db_url))) + .map(|_| may::go!(move || PgConnection::new(db_url))) .collect::>(); let mut clients: Vec<_> = clients.into_iter().map(|t| t.join().unwrap()).collect(); clients.sort_by(|a, b| (a.client.id() % size).cmp(&(b.client.id() % size))); @@ -59,6 +59,7 @@ impl PgConnectionPool { fn get_connection(&self, id: usize) -> PgConnection { let len = self.clients.len(); let connection = &self.clients[id % len]; + // assert_eq!(connection.client.id() % len, id % len); PgConnection { client: connection.client.clone(), statement: connection.statement.clone(), @@ -282,7 +283,7 @@ impl HttpServiceFactory for HttpServer { fn main() { may::config().set_pool_capacity(1000).set_stack_size(0x1000); - println!("Starting http server: 127.0.0.1:8080"); + println!("Starting http server: 0.0.0.1:8080"); let server = HttpServer { db_pool: PgConnectionPool::new( "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world", diff --git a/frameworks/Rust/ntex/Cargo.toml b/frameworks/Rust/ntex/Cargo.toml index b1aff0f6435..ec2173e26e3 100755 --- a/frameworks/Rust/ntex/Cargo.toml +++ b/frameworks/Rust/ntex/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "ntex" -version = "1.0.0" +name = "ntex-bench" +version = "2.0.0" edition = "2018" [[bin]] @@ -8,7 +8,7 @@ name = "ntex" path = "src/main.rs" [[bin]] -name = "ntex-astd" +name = "ntex-compio" path = "src/main.rs" [[bin]] @@ -16,7 +16,7 @@ name = "ntex-db" path = "src/main_db.rs" [[bin]] -name = "ntex-db-astd" +name = "ntex-db-compio" path = "src/main_db.rs" [[bin]] @@ -24,7 +24,7 @@ name = "ntex-plt" path = "src/main_plt.rs" [[bin]] -name = "ntex-plt-astd" +name = "ntex-plt-compio" path = "src/main_plt.rs" [features] @@ -33,26 +33,29 @@ default = [] # tokio runtime tokio = ["ntex/tokio"] -# async-std runtime -async-std = ["ntex/async-std"] +# compio runtime +compio = ["ntex/compio", ] [dependencies] -ntex = "1.0.0" +ntex = "2.4" +ntex-compio = "0.1.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"] } yarte = { version = "0.15", features = ["bytes-buf", "json"] } buf-min = { version = "0.7", features = ["ntex-bytes"] } -env_logger = "0.10" +env_logger = "0.11" nanorand = { version = "0.7", default-features = false, features = ["std", "wyrand", "tls"] } atoi = "2.0" num_cpus = "1.16" -smallvec = "1.11" +smallvec = "1.13" +futures = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" 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-1.0" } +tokio-postgres = { git="https://github.com/fafhrd91/postgres.git", branch="ntex-2" } [profile.release] opt-level = 3 diff --git a/frameworks/Rust/ntex/benchmark_config.json b/frameworks/Rust/ntex/benchmark_config.json index ab6fc091970..9ed29c64b97 100755 --- a/frameworks/Rust/ntex/benchmark_config.json +++ b/frameworks/Rust/ntex/benchmark_config.json @@ -19,7 +19,7 @@ "notes": "", "versus": "" }, - "astd": { + "compio": { "json_url": "/json", "plaintext_url": "/plaintext", "port": 8080, @@ -33,7 +33,7 @@ "webserver": "ntex", "os": "Linux", "database_os": "Linux", - "display_name": "ntex [async-std]", + "display_name": "ntex [compio]", "notes": "", "versus": "" }, @@ -57,7 +57,7 @@ "notes": "", "versus": "" }, - "db-astd": { + "db-compio": { "fortune_url": "/fortunes", "db_url": "/db", "query_url": "/query?q=", @@ -73,7 +73,7 @@ "webserver": "ntex", "os": "Linux", "database_os": "Linux", - "display_name": "ntex [async-std,db]", + "display_name": "ntex [compio,db]", "notes": "", "versus": "" }, @@ -95,7 +95,7 @@ "notes": "", "versus": "" }, - "plt-astd": { + "plt-compio": { "json_url": "/json", "plaintext_url": "/plaintext", "port": 8080, @@ -109,7 +109,7 @@ "webserver": "ntex", "os": "Linux", "database_os": "Linux", - "display_name": "ntex [async-std,platform]", + "display_name": "ntex [compio,platform]", "notes": "", "versus": "" } diff --git a/frameworks/Rust/ntex/config.toml b/frameworks/Rust/ntex/config.toml index 84527c12626..759dca26c70 100644 --- a/frameworks/Rust/ntex/config.toml +++ b/frameworks/Rust/ntex/config.toml @@ -14,7 +14,7 @@ platform = "None" webserver = "ntex" versus = "" -[astd] +[compio] urls.plaintext = "/plaintext" urls.json = "/json" approach = "Realistic" @@ -42,7 +42,7 @@ platform = "None" webserver = "ntex" versus = "" -[db-astd] +[db-compio] urls.db = "/db" urls.query = "/query?q=" urls.update = "/update?q=" @@ -70,7 +70,7 @@ platform = "None" webserver = "ntex" versus = "" -[plt-astd] +[plt-compio] urls.plaintext = "/plaintext" urls.json = "/json" approach = "Realistic" diff --git a/frameworks/Rust/ntex/ntex-astd.dockerfile b/frameworks/Rust/ntex/ntex-compio.dockerfile similarity index 84% rename from frameworks/Rust/ntex/ntex-astd.dockerfile rename to frameworks/Rust/ntex/ntex-compio.dockerfile index be97758bbeb..c825e18078e 100644 --- a/frameworks/Rust/ntex/ntex-astd.dockerfile +++ b/frameworks/Rust/ntex/ntex-compio.dockerfile @@ -9,8 +9,8 @@ ADD ./ /ntex WORKDIR /ntex RUN cargo clean -RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --features="async-std" +RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --features="compio" EXPOSE 8080 -CMD ./target/release/ntex-astd +CMD ./target/release/ntex-compio diff --git a/frameworks/Rust/ntex/ntex-db-astd.dockerfile b/frameworks/Rust/ntex/ntex-db-compio.dockerfile similarity index 84% rename from frameworks/Rust/ntex/ntex-db-astd.dockerfile rename to frameworks/Rust/ntex/ntex-db-compio.dockerfile index e8717d37a50..c892bcdc9fc 100644 --- a/frameworks/Rust/ntex/ntex-db-astd.dockerfile +++ b/frameworks/Rust/ntex/ntex-db-compio.dockerfile @@ -9,8 +9,8 @@ ADD ./ /ntex WORKDIR /ntex RUN cargo clean -RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --features="async-std" +RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --features="compio" EXPOSE 8080 -CMD ./target/release/ntex-db-astd +CMD ./target/release/ntex-db-compio diff --git a/frameworks/Rust/ntex/ntex-plt-astd.dockerfile b/frameworks/Rust/ntex/ntex-plt-compio.dockerfile similarity index 83% rename from frameworks/Rust/ntex/ntex-plt-astd.dockerfile rename to frameworks/Rust/ntex/ntex-plt-compio.dockerfile index 8ec6c7fe636..849e224994b 100644 --- a/frameworks/Rust/ntex/ntex-plt-astd.dockerfile +++ b/frameworks/Rust/ntex/ntex-plt-compio.dockerfile @@ -9,8 +9,8 @@ ADD ./ /ntex WORKDIR /ntex RUN cargo clean -RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --features="async-std" +RUN RUSTFLAGS="-C target-cpu=native" cargo build --release --features="compio" EXPOSE 8080 -CMD ./target/release/ntex-plt-astd +CMD ./target/release/ntex-plt-compio diff --git a/frameworks/Rust/ntex/src/db.rs b/frameworks/Rust/ntex/src/db.rs index 578465a90e2..c5d525eb6de 100644 --- a/frameworks/Rust/ntex/src/db.rs +++ b/frameworks/Rust/ntex/src/db.rs @@ -1,4 +1,5 @@ -use std::{cell::RefCell, fmt::Write as FmtWrite}; +#![allow(clippy::uninit_vec)] +use std::{borrow::Cow, cell::RefCell, fmt::Write as FmtWrite}; use nanorand::{Rng, WyRand}; use ntex::util::{BufMut, Bytes, BytesMut}; @@ -16,9 +17,9 @@ pub struct World { } #[derive(Serialize, Debug)] -pub struct Fortune<'a> { +pub struct Fortune { pub id: i32, - pub message: &'a str, + pub message: Cow<'static, str>, } /// Postgres interface @@ -59,10 +60,7 @@ impl PgConnection { q.push(')'); updates.push(cl.prepare(&q).await.unwrap()); } - let world = cl - .prepare("SELECT id, randomnumber FROM world WHERE id=$1") - .await - .unwrap(); + let world = cl.prepare("SELECT * FROM world WHERE id=$1").await.unwrap(); PgConnection { cl, @@ -70,7 +68,7 @@ impl PgConnection { world, updates, rng: WyRand::new(), - buf: RefCell::new(BytesMut::with_capacity(65535)), + buf: RefCell::new(BytesMut::with_capacity(10 * 1024 * 1024)), } } } @@ -82,7 +80,7 @@ impl PgConnection { let row = self.cl.query_one(&self.world, &[&random_id]).await.unwrap(); let mut body = self.buf.borrow_mut(); - utils::reserve(&mut body); + utils::reserve(&mut body, 1024); World { id: row.get(0), randomnumber: row.get(1), @@ -109,7 +107,7 @@ impl PgConnection { } let mut body = self.buf.borrow_mut(); - utils::reserve(&mut body); + utils::reserve(&mut body, 2 * 1024); body.put_u8(b'['); worlds.iter().for_each(|w| { w.to_bytes_mut(&mut *body); @@ -148,7 +146,7 @@ impl PgConnection { let _ = self.cl.query(&self.updates[num - 1], ¶ms).await; let mut body = self.buf.borrow_mut(); - utils::reserve(&mut body); + utils::reserve(&mut body, 2 * 1024); body.put_u8(b'['); worlds.iter().for_each(|w| { w.to_bytes_mut(&mut *body); @@ -162,19 +160,18 @@ impl PgConnection { pub async fn tell_fortune(&self) -> Bytes { let rows = self.cl.query_raw(&self.fortune, &[]).await.unwrap(); - let mut fortunes = Vec::with_capacity(rows.len() + 1); - fortunes.push(Fortune { + let mut fortunes: SmallVec<[_; 32]> = smallvec::smallvec![Fortune { id: 0, - message: "Additional fortune added at request time.", - }); + message: Cow::Borrowed("Additional fortune added at request time."), + }]; fortunes.extend(rows.iter().map(|row| Fortune { id: row.get(0), - message: row.get(1), + message: Cow::Owned(row.get(1)), })); - fortunes.sort_by(|it, next| it.message.cmp(next.message)); + fortunes.sort_by(|it, next| it.message.cmp(&next.message)); let mut body = std::mem::replace(&mut *self.buf.borrow_mut(), BytesMut::new()); - utils::reserve(&mut body); + utils::reserve(&mut body, 4 * 1024); ywrite_html!(body, "{{> fortune }}"); let result = body.split().freeze(); let _ = std::mem::replace(&mut *self.buf.borrow_mut(), body); diff --git a/frameworks/Rust/ntex/src/main.rs b/frameworks/Rust/ntex/src/main.rs index c9fd1e80245..218ed03c727 100644 --- a/frameworks/Rust/ntex/src/main.rs +++ b/frameworks/Rust/ntex/src/main.rs @@ -1,5 +1,5 @@ #[global_allocator] -static GLOBAL: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; use ntex::http::header::{CONTENT_TYPE, SERVER}; use ntex::{http, time::Seconds, util::BytesMut, util::PoolId, web}; diff --git a/frameworks/Rust/ntex/src/main_db.rs b/frameworks/Rust/ntex/src/main_db.rs index 437fd70b9e1..78c21fa7132 100644 --- a/frameworks/Rust/ntex/src/main_db.rs +++ b/frameworks/Rust/ntex/src/main_db.rs @@ -1,7 +1,7 @@ #[cfg(not(target_os = "macos"))] #[global_allocator] -static GLOBAL: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; -// static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; +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}; diff --git a/frameworks/Rust/ntex/src/main_plt.rs b/frameworks/Rust/ntex/src/main_plt.rs index 4e9279d4ebf..b6c435f6773 100644 --- a/frameworks/Rust/ntex/src/main_plt.rs +++ b/frameworks/Rust/ntex/src/main_plt.rs @@ -1,5 +1,6 @@ #[global_allocator] -static GLOBAL: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; +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}; @@ -35,7 +36,7 @@ impl Future for App { Ok((req, _)) => { let _ = this.io.with_write_buf(|buf| { buf.with_bytes_mut(|buf| { - utils::reserve(buf); + utils::reserve(buf, 2 * 1024); match req.path() { "/json" => { buf.extend_from_slice(JSON); diff --git a/frameworks/Rust/ntex/src/utils.rs b/frameworks/Rust/ntex/src/utils.rs index 5792506dd71..719d5962dd4 100644 --- a/frameworks/Rust/ntex/src/utils.rs +++ b/frameworks/Rust/ntex/src/utils.rs @@ -11,7 +11,6 @@ pub const HDR_HTML_CONTENT_TYPE: HeaderValue = HeaderValue::from_static("text/html; charset=utf-8"); pub const BODY_PLAIN_TEXT: Bytes = Bytes::from_static(b"Hello, World!"); -const LW: usize = 1024; const HW: usize = 128 * 1024; pub const SIZE: usize = 27; @@ -25,9 +24,9 @@ pub fn get_query_param(query: Option<&str>) -> usize { cmp::min(500, cmp::max(1, q) as usize) } -pub fn reserve(buf: &mut BytesMut) { +pub fn reserve(buf: &mut BytesMut, lw: usize) { let remaining = buf.remaining_mut(); - if remaining < LW { + if remaining < lw { buf.reserve(HW); } } diff --git a/frameworks/Rust/ohkami/Cargo.lock b/frameworks/Rust/ohkami/Cargo.lock index b0bc7617d71..e379d121817 100644 --- a/frameworks/Rust/ohkami/Cargo.lock +++ b/frameworks/Rust/ohkami/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -24,7 +24,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", "once_cell", "version_check", "zerocopy", @@ -66,15 +65,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -87,9 +86,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -105,9 +104,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -123,9 +122,9 @@ dependencies = [ [[package]] name = "byte_reader" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac67f5455e694831246ed9a2d62c98dbb7586281dcfaba6621888ac9b576df" +checksum = "3aad623c0c9416ec94524edd23af3f3e2fd16d1ec7d41c940084c05f77e35c96" [[package]] name = "byteorder" @@ -135,15 +134,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.0.95" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -151,6 +153,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -175,15 +186,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -214,9 +225,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -241,15 +252,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -278,9 +289,9 @@ checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" dependencies = [ "serde", ] @@ -293,9 +304,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -314,21 +325,20 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" - -[[package]] -name = "finl_unicode" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "flume" @@ -338,7 +348,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -417,7 +427,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -461,9 +471,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -472,15 +482,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -488,21 +498,18 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ "hashbrown", ] [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -555,23 +562,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -580,18 +578,18 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin", ] [[package]] name = "libc" -version = "0.2.153" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libm" @@ -601,9 +599,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libsqlite3-sys" -version = "0.27.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "pkg-config", @@ -612,15 +610,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -628,9 +626,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "md-5" @@ -644,9 +642,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" @@ -656,31 +654,31 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -730,9 +728,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -741,38 +739,28 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] -[[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.32.2" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] [[package]] name = "ohkami" -version = "0.18.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d810447a32af4750a45183a4915db5ca4183bb6d44b8e7731e63f627c581d93e" +checksum = "aa5507d0f2230d8fbbec9163207da1219ac513441269465a8d532e0bf5018411" dependencies = [ "byte_reader", "hmac", @@ -787,7 +775,7 @@ dependencies = [ [[package]] name = "ohkami_framework_benchmarks" -version = "0.18.2" +version = "0.20.0" dependencies = [ "futures-util", "ohkami", @@ -799,9 +787,9 @@ dependencies = [ [[package]] name = "ohkami_lib" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4433e6c4c6c67a72afc306783ca8c641e69c9d171279fcab51f1f0c2d875e121" +checksum = "889b290a5ca6f0e2d1b1bae72d0ec1fb768d84cf246c532e6f7c65ff962e65e3" dependencies = [ "byte_reader", "percent-encoding", @@ -810,9 +798,9 @@ dependencies = [ [[package]] name = "ohkami_macros" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffed4cd7e8c12a62c077273cb0f0fedf67ca3912488e5de1b325be85ca86bb06" +checksum = "8a231d9afdadeab9fc5c850093377c65ecd30512692de0efec51f708dff5be12" dependencies = [ "proc-macro2", "quote", @@ -827,11 +815,11 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -848,7 +836,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -859,9 +847,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -869,11 +857,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -881,22 +875,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem-rfc7468" @@ -954,9 +948,12 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" @@ -970,18 +967,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1025,11 +1022,20 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1039,9 +1045,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1050,9 +1056,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rsa" @@ -1076,32 +1082,32 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1110,9 +1116,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" @@ -1131,11 +1137,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -1144,9 +1150,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -1154,36 +1160,49 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ + "form_urlencoded", "itoa", "ryu", "serde", @@ -1211,6 +1230,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -1244,23 +1269,20 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -1282,20 +1304,19 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "itertools", "nom", "unicode_categories", ] [[package]] name = "sqlx" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +checksum = "fcfa89bea9500db4a0d038513d7a060566bfc51d46d1c014847049a45cce85e8" dependencies = [ "sqlx-core", "sqlx-macros", @@ -1306,11 +1327,10 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +checksum = "d06e2f2bd861719b1f3f0c7dbe1d80c30bf59e76cf019f07d9014ed7eefb8e08" dependencies = [ - "ahash", "atoi", "byteorder", "bytes", @@ -1323,6 +1343,7 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", + "hashbrown", "hashlink", "hex", "indexmap", @@ -1346,22 +1367,22 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +checksum = "2f998a9defdbd48ed005a89362bd40dd2117502f15294f61c8d47034107dbbdc" dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] name = "sqlx-macros-core" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +checksum = "3d100558134176a2629d46cec0c8891ba0be8910f7896abfdb75ef4ab6f4e7ce" dependencies = [ "dotenvy", "either", @@ -1377,7 +1398,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 1.0.109", + "syn 2.0.77", "tempfile", "tokio", "url", @@ -1385,13 +1406,13 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" +checksum = "936cac0ab331b14cb3921c62156d913e4c15b74fb6ec0f3146bd4ef6e4fb3c12" dependencies = [ "atoi", "base64", - "bitflags 2.5.0", + "bitflags 2.6.0", "byteorder", "bytes", "crc", @@ -1427,13 +1448,13 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" +checksum = "9734dbce698c67ecf67c442f768a5e90a49b2a4d61a9f1d59f73874bd4cf0710" dependencies = [ "atoi", "base64", - "bitflags 2.5.0", + "bitflags 2.6.0", "byteorder", "crc", "dotenvy", @@ -1465,9 +1486,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +checksum = "a75b419c3c1b1697833dd927bdc4c6545a620bc1bbafabd44e1efbe9afcd337e" dependencies = [ "atoi", "flume", @@ -1480,28 +1501,28 @@ dependencies = [ "log", "percent-encoding", "serde", + "serde_urlencoded", "sqlx-core", "tracing", "url", - "urlencoding", ] [[package]] name = "stringprep" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -1516,9 +1537,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -1527,41 +1548,42 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -1574,32 +1596,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -1642,7 +1663,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] @@ -1682,22 +1703,22 @@ dependencies = [ ] [[package]] -name = "unicode-segmentation" -version = "1.11.0" +name = "unicode-properties" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "unicode_categories" @@ -1707,21 +1728,15 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "v_eval" version = "0.6.0" @@ -1746,9 +1761,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1768,7 +1783,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "redox_syscall", + "redox_syscall 0.4.1", "wasite", ] @@ -1809,7 +1824,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1829,18 +1853,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1851,9 +1875,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1863,9 +1887,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1875,15 +1899,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1893,9 +1917,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1905,9 +1929,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1917,9 +1941,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1929,9 +1953,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yansi-term" @@ -2029,26 +2053,27 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.77", ] [[package]] name = "zeroize" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63381fa6624bf92130a6b87c0d07380116f80b565c42cf0d754136f0238359ef" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/frameworks/Rust/ohkami/Cargo.toml b/frameworks/Rust/ohkami/Cargo.toml index 8a168f5c413..04e08b92b0c 100644 --- a/frameworks/Rust/ohkami/Cargo.toml +++ b/frameworks/Rust/ohkami/Cargo.toml @@ -1,13 +1,24 @@ [package] name = "ohkami_framework_benchmarks" -version = "0.18.2" +version = "0.20.0" edition = "2021" authors = ["kanarus "] [dependencies] -ohkami = { version = "=0.18.2", features = ["rt_tokio"] } -tokio = { version = "1.37.0" , features = ["full"] } +ohkami = { version = "=0.20.0", features = ["rt_tokio"] } +tokio = { version = "1.40.0" , features = ["full"] } rand = { version = "0.8.5" , features = ["small_rng"] } -sqlx = { version = "0.7.4" , features = ["postgres", "macros", "runtime-tokio-native-tls"] } +sqlx = { version = "0.8.1" , features = ["postgres", "macros", "runtime-tokio-native-tls"] } yarte = { version = "0.15.7" } futures-util = { version = "0.3.30" } + +[profile.release] +opt-level = 3 +debug = false +debug-assertions = false +lto = true +panic = "abort" +incremental = false +codegen-units = 1 +rpath = false +strip = false \ No newline at end of file diff --git a/frameworks/Rust/ohkami/README.md b/frameworks/Rust/ohkami/README.md index 21b85f2cc50..d9fda426ed3 100644 --- a/frameworks/Rust/ohkami/README.md +++ b/frameworks/Rust/ohkami/README.md @@ -1,4 +1,4 @@ -# [ohkami](https://github.com/kana-rus/ohkami) - Intuitive and Declarative Web Framework for Rust +# [Ohkami](https://github.com/kana-rus/ohkami) - Intuitive and Declarative Web Framework for Rust ## Description diff --git a/frameworks/Rust/ohkami/ohkami.dockerfile b/frameworks/Rust/ohkami/ohkami.dockerfile index 97a69dd07ae..287f293a061 100644 --- a/frameworks/Rust/ohkami/ohkami.dockerfile +++ b/frameworks/Rust/ohkami/ohkami.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-slim-buster +FROM rust:1.80-slim-bullseye WORKDIR /ohkami_framework_benchmarks ENV DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world diff --git a/frameworks/Rust/ohkami/src/fangs.rs b/frameworks/Rust/ohkami/src/fangs.rs index 46a19808f46..2dd8e27e5d3 100644 --- a/frameworks/Rust/ohkami/src/fangs.rs +++ b/frameworks/Rust/ohkami/src/fangs.rs @@ -1,5 +1,4 @@ use ohkami::prelude::*; -use crate::Postgres; #[derive(Clone)] @@ -10,41 +9,3 @@ impl FangAction for SetServer { res.headers.set().Server("ohkami"); } } - -impl Postgres { - pub async fn init() -> impl FangAction { - #[derive(Clone)] - pub struct UsePostgres(Postgres); - impl FangAction for UsePostgres { - #[inline(always)] - async fn fore<'a>(&'a self, req: &'a mut Request) -> Result<(), Response> { - Ok(req.memorize(self.0.clone())) - } - } - - macro_rules! load_env { - ($($name:ident as $t:ty)*) => {$( - #[allow(non_snake_case)] - let $name = ::std::env::var(stringify!($name)) - .expect(concat!( - "Failed to load environment variable ", - "`", stringify!($name), "`" - )) - .parse::<$t>() - .unwrap(); - )*}; - } load_env! { - MAX_CONNECTIONS as u32 - MIN_CONNECTIONS as u32 - DATABASE_URL as String - } - - let pool = sqlx::postgres::PgPoolOptions::new() - .max_connections(MAX_CONNECTIONS) - .min_connections(MIN_CONNECTIONS) - .connect(&DATABASE_URL).await - .unwrap(); - - UsePostgres(pool.into()) - } -} diff --git a/frameworks/Rust/ohkami/src/main.rs b/frameworks/Rust/ohkami/src/main.rs index 7473338dc5b..96cc9cc2fbc 100644 --- a/frameworks/Rust/ohkami/src/main.rs +++ b/frameworks/Rust/ohkami/src/main.rs @@ -11,12 +11,16 @@ mod templates; use templates::FortunesTemplate; use ohkami::prelude::*; +use ohkami::format::{JSON, Query}; use ohkami::Memory; #[tokio::main] async fn main() { - Ohkami::with((SetServer, Postgres::init().await), ( + Ohkami::with(( + SetServer, + Memory::new(Postgres::new().await), + ), ( "/json" .GET(json_serialization), "/db" .GET(single_database_query), "/queries" .GET(multiple_database_query), @@ -26,40 +30,44 @@ async fn main() { )).howl("0.0.0.0:8000").await } -async fn json_serialization() -> Message { - Message { +async fn json_serialization() -> JSON { + JSON(Message { message: "Hello, World!" - } + }) } -async fn single_database_query(p: Memory<'_, Postgres>) -> World { - p.select_random_world().await +async fn single_database_query(p: Memory<'_, Postgres>) -> JSON { + let world = p.select_random_world().await; + JSON(world) } -async fn multiple_database_query(q: WorldsQuery<'_>, p: Memory<'_, Postgres>) -> Vec { +async fn multiple_database_query( + Query(q): Query>, + p: Memory<'_, Postgres> +) -> JSON> { let n = q.parse(); - p.select_n_random_worlds(n).await + let worlds = p.select_n_random_worlds(n).await; + JSON(worlds) } async fn fortunes(p: Memory<'_, Postgres>) -> FortunesTemplate { let mut fortunes = p.select_all_fortunes().await; - fortunes.push(Fortune { id: 0, message: String::from("Additional fortune added at request time."), }); fortunes.sort_unstable_by(|a, b| str::cmp(&a.message, &b.message)); - FortunesTemplate { fortunes } } -async fn database_updates(q: WorldsQuery<'_>, p: Memory<'_, Postgres>) -> Vec { +async fn database_updates( + Query(q): Query>, + p: Memory<'_, Postgres> +) -> JSON> { let n = q.parse(); let mut worlds = p.select_n_random_worlds(n).await; - p.update_random_ids_of_worlds(&mut worlds).await; - - worlds + JSON(worlds) } async fn plaintext() -> &'static str { diff --git a/frameworks/Rust/ohkami/src/models.rs b/frameworks/Rust/ohkami/src/models.rs index 55eb8f8fa6d..874dd22a5d7 100644 --- a/frameworks/Rust/ohkami/src/models.rs +++ b/frameworks/Rust/ohkami/src/models.rs @@ -1,8 +1,7 @@ -use ohkami::typed::{Payload, Query}; -use ohkami::builtin::payload::JSON; +use ohkami::serde::{Serialize, Deserialize}; -#[Payload(JSON/S)] +#[derive(Serialize)] pub struct Message { pub message: &'static str, } @@ -14,14 +13,14 @@ pub struct Fortune { } #[derive(sqlx::FromRow)] -#[Payload(JSON/S)] +#[derive(Serialize)] pub struct World { pub id: i32, #[serde(rename="randomNumber")] pub randomnumber: i32, } -#[Query] +#[derive(Deserialize)] pub struct WorldsQuery<'q> { q: Option<&'q str>, } diff --git a/frameworks/Rust/ohkami/src/postgres.rs b/frameworks/Rust/ohkami/src/postgres.rs index 7f29317d1ba..c320e2516e3 100644 --- a/frameworks/Rust/ohkami/src/postgres.rs +++ b/frameworks/Rust/ohkami/src/postgres.rs @@ -6,9 +6,32 @@ use crate::models::{World, Fortune}; #[derive(Clone)] pub struct Postgres(sqlx::PgPool); -impl From for Postgres { - fn from(pgpool: sqlx::PgPool) -> Self { - Self(pgpool) +impl Postgres { + pub async fn new() -> Self { + macro_rules! load_env { + ($($name:ident as $t:ty)*) => {$( + #[allow(non_snake_case)] + let $name = ::std::env::var(stringify!($name)) + .expect(concat!( + "Failed to load environment variable ", + "`", stringify!($name), "`" + )) + .parse::<$t>() + .unwrap(); + )*}; + } load_env! { + MAX_CONNECTIONS as u32 + MIN_CONNECTIONS as u32 + DATABASE_URL as String + } + + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(MAX_CONNECTIONS) + .min_connections(MIN_CONNECTIONS) + .connect(&DATABASE_URL).await + .unwrap(); + + Self(pool) } } @@ -16,16 +39,14 @@ impl Postgres { pub async fn select_random_world(&self) -> World { let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - sqlx::query_as( - "SELECT id, randomnumber FROM World WHERE id = $1") + sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1") .bind((rng.gen::() % 10_000 + 1) as i32) .fetch_one(&self.0).await .expect("Failed to fetch a world") } pub async fn select_all_fortunes(&self) -> Vec { - sqlx::query_as( - "SELECT id, message FROM Fortune") + sqlx::query_as("SELECT id, message FROM Fortune") .fetch_all(&self.0).await .expect("Failed to fetch fortunes") } @@ -36,8 +57,7 @@ impl Postgres { let selects = FuturesUnordered::new(); for _ in 0..n { selects.push( - sqlx::query_as( - "SELECT id, randomnumber FROM World WHERE id = $1") + sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1") .bind((rng.gen::() % 10_000 + 1) as i32) .fetch_one(&self.0) ) diff --git a/frameworks/Rust/tide/Cargo.lock b/frameworks/Rust/tide/Cargo.lock index e9aa1d8598d..7ed70839854 100644 --- a/frameworks/Rust/tide/Cargo.lock +++ b/frameworks/Rust/tide/Cargo.lock @@ -93,7 +93,7 @@ checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522" dependencies = [ "askama_shared", "proc-macro2", - "syn", + "syn 1.0.60", ] [[package]] @@ -116,7 +116,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 1.0.60", "toml", ] @@ -127,7 +127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ "quote", - "syn", + "syn 1.0.60", ] [[package]] @@ -331,7 +331,7 @@ checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.60", ] [[package]] @@ -380,6 +380,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bitvec" version = "0.19.4" @@ -609,7 +615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" dependencies = [ "quote", - "syn", + "syn 1.0.60", ] [[package]] @@ -621,6 +627,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.75", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.75", +] + [[package]] name = "dashmap" version = "4.0.2" @@ -639,26 +680,38 @@ checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" [[package]] name = "diesel" -version = "1.4.6" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "047bfc4d5c3bd2ef6ca6f981941046113524b9a9f9a7cbdfdd7ff40f58e6f542" +checksum = "65e13bab2796f412722112327f3e575601a3e9cdcbe426f0d30dbf43f3f5dc71" dependencies = [ - "bitflags", + "bitflags 2.6.0", "byteorder", "diesel_derives", + "itoa 1.0.11", "pq-sys", "r2d2", ] [[package]] name = "diesel_derives" -version = "1.4.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", - "syn", + "syn 2.0.75", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" +dependencies = [ + "syn 2.0.75", ] [[package]] @@ -676,6 +729,26 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "dsl_auto_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn 2.0.75", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "event-listener" version = "2.5.1" @@ -707,6 +780,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -768,7 +847,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn", + "syn 1.0.60", ] [[package]] @@ -837,6 +916,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.18" @@ -923,6 +1008,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.2.2" @@ -955,6 +1046,12 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "js-sys" version = "0.3.48" @@ -986,7 +1083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" dependencies = [ "arrayvec", - "bitflags", + "bitflags 1.2.1", "cfg-if 1.0.0", "ryu", "static_assertions", @@ -1153,7 +1250,7 @@ checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.60", ] [[package]] @@ -1227,18 +1324,18 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1317,7 +1414,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" dependencies = [ - "bitflags", + "bitflags 1.2.1", ] [[package]] @@ -1388,7 +1485,7 @@ checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.60", ] [[package]] @@ -1397,7 +1494,7 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43535db9747a4ba938c0ce0a98cc631a46ebf943c9e1d604e091df6007620bf6" dependencies = [ - "itoa", + "itoa 0.4.7", "ryu", "serde", ] @@ -1421,7 +1518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" dependencies = [ "form_urlencoded", - "itoa", + "itoa 0.4.7", "ryu", "serde", ] @@ -1541,7 +1638,7 @@ dependencies = [ "quote", "serde", "serde_derive", - "syn", + "syn 1.0.60", ] [[package]] @@ -1557,7 +1654,7 @@ dependencies = [ "serde_derive", "serde_json", "sha1", - "syn", + "syn 1.0.60", ] [[package]] @@ -1566,6 +1663,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.4.0" @@ -1589,6 +1692,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn" +version = "2.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tap" version = "1.0.1" @@ -1612,7 +1726,7 @@ checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.60", ] [[package]] @@ -1697,7 +1811,7 @@ dependencies = [ "proc-macro2", "quote", "standback", - "syn", + "syn 1.0.60", ] [[package]] @@ -1739,6 +1853,12 @@ dependencies = [ "matches", ] +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "unicode-normalization" version = "0.1.17" @@ -1840,7 +1960,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 1.0.60", "wasm-bindgen-shared", ] @@ -1874,7 +1994,7 @@ checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/frameworks/Rust/tide/Cargo.toml b/frameworks/Rust/tide/Cargo.toml index 80c05675a77..18b2aeea38c 100644 --- a/frameworks/Rust/tide/Cargo.toml +++ b/frameworks/Rust/tide/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" askama = "0.10.5" async-std = { version = "1.9.0", features = ["attributes"] } async-trait = "0.1.42" -diesel = { version = "1.4.6", features = ["postgres", "r2d2"] } +diesel = { version = "2.2.3", features = ["postgres", "r2d2"] } http-types = "2.10.0" rand = { version = "0.7", features = ["small_rng"] } serde = { version = "1.0.123", features = ["derive"] } diff --git a/frameworks/Rust/viz/Cargo.toml b/frameworks/Rust/viz/Cargo.toml index f28595febb5..b2e90cb4836 100644 --- a/frameworks/Rust/viz/Cargo.toml +++ b/frameworks/Rust/viz/Cargo.toml @@ -24,23 +24,28 @@ path = "src/main_diesel.rs" required-features = ["diesel", "diesel-async", "sailfish"] [dependencies] -viz = "0.8" -hyper = "1.0" +viz = "0.9" +hyper = "1.4" hyper-util = "0.1" atoi = "2.0" serde = { version = "1.0", features = ["derive"] } -nanorand = "0.7" rand = { version = "0.8", features = ["small_rng"] } thiserror = "1.0" futures-util = "0.3" +[target.'cfg(not(unix))'.dependencies] +nanorand = { version = "0.7" } + +[target.'cfg(unix)'.dependencies] +nanorand = { version = "0.7", features = ["getrandom"] } + tokio = { version = "1", features = ["full"] } tokio-postgres = { version = "0.7", optional = true } -sqlx = { version = "0.7", features = [ +sqlx = { version = "0.8", features = [ "postgres", "macros", "runtime-tokio", - "tls-native-tls" + "tls-native-tls", ], optional = true } diesel = { version = "2.2", default-features = false, features = [ "i-implement-a-third-party-backend-and-opt-into-breaking-changes", @@ -53,7 +58,7 @@ diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "74 yarte = { version = "0.15", features = ["bytes-buf", "json"], optional = true } markup = { version = "0.15", optional = true } v_htmlescape = { version = "0.15", optional = true } -sailfish = { version = "0.8", optional = true } +sailfish = { version = "0.9", optional = true } [profile.release] lto = true diff --git a/frameworks/Rust/viz/src/db_sqlx.rs b/frameworks/Rust/viz/src/db_sqlx.rs index af8322e9d1d..40c39290265 100644 --- a/frameworks/Rust/viz/src/db_sqlx.rs +++ b/frameworks/Rust/viz/src/db_sqlx.rs @@ -60,7 +60,7 @@ pub async fn get_world( id: i32, ) -> Result { let mut args = PgArguments::default(); - args.add(id); + let _ = args.add(id); let world = sqlx::query_as_with("SELECT id, randomnumber FROM World WHERE id = $1", args) @@ -86,8 +86,8 @@ pub async fn update_worlds( for w in &worlds { let mut args = PgArguments::default(); - args.add(w.randomnumber); - args.add(w.id); + let _ = args.add(w.randomnumber); + let _ = args.add(w.id); sqlx::query_with("UPDATE World SET randomNumber = $1 WHERE id = $2", args) .execute(&mut *conn) diff --git a/frameworks/Rust/viz/templates/fortune.stpl b/frameworks/Rust/viz/templates/fortune.stpl index eb1abe6a4fa..874b48cc6f5 100644 --- a/frameworks/Rust/viz/templates/fortune.stpl +++ b/frameworks/Rust/viz/templates/fortune.stpl @@ -4,7 +4,7 @@ - <% for item in items { %><% } %> + <% for item in self.items { %><% } %>
idmessage
<%= item.id %><%= &*item.message %>
<%= item.id %><%= &*item.message %>
- \ No newline at end of file + diff --git a/frameworks/Rust/xitca-web/.cargo/config.toml b/frameworks/Rust/xitca-web/.cargo/config.toml index 7d3c09f8a1a..df736010a76 100644 --- a/frameworks/Rust/xitca-web/.cargo/config.toml +++ b/frameworks/Rust/xitca-web/.cargo/config.toml @@ -1,5 +1,5 @@ [build] -rustflags = ["-C", "target-cpu=native"] +rustflags = ["-C", "target-cpu=native", "--cfg", "tokio_unstable"] incremental = false [target.wasm32-wasip1-threads] diff --git a/frameworks/Rust/xitca-web/Cargo.lock b/frameworks/Rust/xitca-web/Cargo.lock index 0bce6f973f8..070a2e67550 100644 --- a/frameworks/Rust/xitca-web/Cargo.lock +++ b/frameworks/Rust/xitca-web/Cargo.lock @@ -1,27 +1,27 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -39,80 +39,42 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", -] +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bb8" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10cf871f3ff2ce56432fddc2615ac7acc3aa22ca321f8fea800846fbb32f188" +dependencies = [ + "async-trait", + "futures-util", + "parking_lot", + "tokio", +] [[package]] name = "bitflags" @@ -122,9 +84,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -135,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -143,15 +111,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -161,9 +132,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -178,13 +149,48 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "diesel" -version = "2.1.6" +version = "2.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "byteorder", "diesel_derives", "itoa", @@ -192,13 +198,28 @@ dependencies = [ "r2d2", ] +[[package]] +name = "diesel-async" +version = "0.5.0" +source = "git+https://github.com/weiznich/diesel_async?rev=5b8262b#5b8262b86d8ed0e13adbbc4aee39500b9931ef8d" +dependencies = [ + "async-trait", + "bb8", + "diesel", + "futures-util", + "scoped-futures", + "tokio", + "tokio-postgres", +] + [[package]] name = "diesel_derives" -version = "2.1.4" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", "syn", @@ -206,9 +227,9 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ "syn", ] @@ -225,28 +246,30 @@ dependencies = [ ] [[package]] -name = "fallible-iterator" -version = "0.2.0" +name = "dsl_auto_type" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "filetime" -version = "0.2.23" +name = "either" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "finl_unicode" -version = "1.2.0" +name = "fallible-iterator" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fnv" @@ -263,28 +286,58 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", + "futures-sink", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -310,9 +363,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -329,15 +388,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "http" version = "1.1.0" @@ -349,34 +399,11 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -384,11 +411,17 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "io-uring" -version = "0.5.13" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1e1a01cfb924fd8c5c43b6827965db394f5a3a16c599ce03452266e1cf984c" +checksum = "595a0399f411a508feb2ec1e970a4a30c249351e30208960d58298de8660b0e5" dependencies = [ "bitflags 1.3.2", "libc", @@ -406,17 +439,26 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libmimalloc-sys" -version = "0.1.38" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6" +checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" dependencies = [ "cc", "libc", @@ -434,15 +476,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "md-5" @@ -456,51 +492,39 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mimalloc" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176" +checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633" dependencies = [ "libmimalloc-sys", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +version = "1.0.2" +source = "git+https://github.com/fakeshadow/mio?rev=9bae6012b7ecfc6083350785f71a5e8265358178#9bae6012b7ecfc6083350785f71a5e8265358178" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" - [[package]] name = "num-traits" version = "0.2.19" @@ -510,36 +534,26 @@ 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.32.2" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -553,9 +567,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets", ] [[package]] @@ -565,23 +579,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "pin-project" -version = "1.1.5" +name = "phf" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "pin-project-internal", + "phf_shared", ] [[package]] -name = "pin-project-internal" -version = "1.1.5" +name = "phf_shared" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "proc-macro2", - "quote", - "syn", + "siphasher", ] [[package]] @@ -598,9 +610,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "postgres-protocol" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" +checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" dependencies = [ "base64", "byteorder", @@ -616,9 +628,9 @@ dependencies = [ [[package]] name = "postgres-types" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" +checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" dependencies = [ "bytes", "fallible-iterator", @@ -627,33 +639,36 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "pq-sys" -version = "0.4.8" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" +checksum = "f6cc05d7ea95200187117196eee9edd0644424911821aeb28a18ce60ea0b8793" dependencies = [ "vcpkg", ] [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -701,20 +716,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -723,12 +729,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -737,40 +737,15 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "sailfish" -version = "0.8.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd5f4680149b62b3478f6af08a8f1c37794bc1bc577e28874a4d0c70084d600" +checksum = "d4d5cd6d4f24f3ab107e949ab424738cf55b03deddce3b184c46985d7b1394ef" dependencies = [ "itoap", "ryu", - "sailfish-macros", "version_check", ] -[[package]] -name = "sailfish-compiler" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67087aca4a3886686a88cee6835089c53e6143a0b8c5be01e63e4fe2f6dfe7cb" -dependencies = [ - "filetime", - "home", - "memchr", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sailfish-macros" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47e31910c5f9230e99992568d05a5968fe4f42a635c3f912c993e9f66a619a5" -dependencies = [ - "proc-macro2", - "sailfish-compiler", -] - [[package]] name = "scheduled-thread-pool" version = "0.2.7" @@ -781,10 +756,14 @@ dependencies = [ ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "scoped-futures" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" +dependencies = [ + "cfg-if", + "pin-utils", +] [[package]] name = "scopeguard" @@ -794,18 +773,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -814,25 +793,16 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -856,6 +826,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -865,6 +841,12 @@ dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -897,54 +879,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "stringprep" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.65" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -957,86 +933,80 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +version = "1.40.0" +source = "git+https://github.com/tokio-rs/tokio.git?rev=512e9de#512e9decfb683d22f4a145459142542caa0894c9" dependencies = [ "backtrace", + "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2 0.5.7", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] -name = "tokio-uring" -version = "0.4.0" +name = "tokio-postgres" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d5e02bb137e030b3a547c65a3bd2f1836d66a97369fdcc69034002b10e155ef" +checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" dependencies = [ + "async-trait", + "byteorder", "bytes", - "io-uring", - "libc", - "scoped-tls", - "slab", - "socket2 0.4.10", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2 0.5.7", "tokio", + "tokio-util", + "whoami", ] [[package]] -name = "tower" -version = "0.4.13" +name = "tokio-uring" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "748482e3e13584a34664a710168ad5068e8cb1d968aa4ffa887e83ca6dd27967" dependencies = [ - "futures-core", + "bytes", "futures-util", - "pin-project", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", + "io-uring", + "libc", + "slab", + "socket2 0.4.10", + "tokio", ] [[package]] -name = "tower-http" -version = "0.5.2" +name = "tokio-util" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ - "bitflags 2.5.0", "bytes", - "http", - "http-body", - "http-body-util", + "futures-core", + "futures-sink", "pin-project-lite", - "tower-layer", - "tower-service", + "tokio", ] -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-core", ] @@ -1046,9 +1016,6 @@ name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] [[package]] name = "typenum" @@ -1058,25 +1025,31 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1085,9 +1058,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1096,171 +1069,186 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "winapi" -version = "0.3.9" +name = "wasite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen-backend" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "wasm-bindgen-macro" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ - "windows-targets 0.48.5", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "wasm-bindgen-macro-support" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ - "windows-targets 0.52.5", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "wasm-bindgen-shared" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] -name = "windows-targets" -version = "0.52.5" +name = "web-sys" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "whoami" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows_i686_gnu" -version = "0.48.5" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] [[package]] -name = "windows_i686_gnu" -version = "0.52.5" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] [[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" +name = "windows_aarch64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_aarch64_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] -name = "windows_i686_msvc" -version = "0.52.5" +name = "windows_i686_gnu" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" +name = "windows_i686_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "windows_x86_64_gnu" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xitca-codegen" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866906a5f280481ef022ccdec1640730550304bb86b016815d9982fde2f48e3e" +version = "0.4.0" +source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" dependencies = [ "quote", "syn", @@ -1268,9 +1256,8 @@ dependencies = [ [[package]] name = "xitca-http" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d5fd258cd6cd9d677cb94273da69fafee7460bbbd001c92a73c167149856e46" +version = "0.7.0" +source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" dependencies = [ "futures-core", "http", @@ -1290,9 +1277,9 @@ dependencies = [ [[package]] name = "xitca-io" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da690dc253320dae7ffbb70e7fa9c5e52ef79476dd41f5d52b9114c8b58d7126" +checksum = "19b91b7a5ff9e3bed167b7e3bcc7b4462d2cb16d05e3ae913dbc384e463fdd7f" dependencies = [ "bytes", "tokio", @@ -1302,34 +1289,63 @@ dependencies = [ [[package]] name = "xitca-postgres" -version = "0.1.0" -source = "git+https://github.com/HFQR/xitca-web.git?rev=ea1f5a2447e0969a6dff84eac9ff9ff90dbc7ed1#ea1f5a2447e0969a6dff84eac9ff9ff90dbc7ed1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46046cb7a3d4fcfb5c858bab0651c73bb45c0c5f9f0f53dd4eb991f2d6e5d6f4" dependencies = [ "fallible-iterator", + "futures-core", "percent-encoding", "postgres-protocol", "postgres-types", "tokio", "tracing", "xitca-io", - "xitca-service", "xitca-unsafe-collection", ] +[[package]] +name = "xitca-postgres" +version = "0.3.0" +source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" +dependencies = [ + "fallible-iterator", + "futures-core", + "percent-encoding", + "postgres-protocol", + "postgres-types", + "tokio", + "tracing", + "xitca-io", + "xitca-unsafe-collection", +] + +[[package]] +name = "xitca-postgres-diesel" +version = "0.1.0" +source = "git+https://github.com/fakeshadow/xitca-postgres-diesel?rev=ae93ee9#ae93ee95277e281fb87b351c42bfc2fc5a56703a" +dependencies = [ + "diesel", + "diesel-async", + "futures-core", + "scoped-futures", + "tokio", + "xitca-postgres 0.2.1", +] + [[package]] name = "xitca-router" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687a3fb0a32b89524fab7d780d4cc66942b8ee6a493a7f2ff78384fe677b8e09" +checksum = "35a771113f381c9a2f5ae1096b70d629ed241a1a473304ea902258c3d528f536" dependencies = [ "xitca-unsafe-collection", ] [[package]] name = "xitca-server" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e144aca50286d05f7450045d6b6eebe2157ed11bc5821d926fc276280113c94" +version = "0.5.0" +source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" dependencies = [ "socket2 0.5.7", "tokio", @@ -1342,15 +1358,14 @@ dependencies = [ [[package]] name = "xitca-service" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a4a38548b14925111dd99560f0a10d1eb9e3e117fa5471c35387ed6f77b58c" +version = "0.3.0" +source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" [[package]] name = "xitca-unsafe-collection" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552a6bf21a5d0dc470644cb3b99f98f44bd414cd6fcca74610465d8196b1d23e" +checksum = "467b95b08735dcd7061be626d02aea062bc0b99918bc9de11b8fc15d901158ae" dependencies = [ "bytes", ] @@ -1360,45 +1375,63 @@ name = "xitca-web" version = "0.1.0" dependencies = [ "atoi", - "axum", "diesel", + "diesel-async", "futures-core", - "http-body", + "futures-util", + "httparse", "mimalloc", - "nanorand", + "rand", "sailfish", "serde", "serde_json", "tokio", - "tower", - "tower-http", + "tokio-uring", "xitca-http", "xitca-io", - "xitca-postgres", + "xitca-postgres 0.3.0", + "xitca-postgres-diesel", "xitca-server", "xitca-service", "xitca-unsafe-collection", - "xitca-web 0.5.0", + "xitca-web 0.7.0", ] [[package]] name = "xitca-web" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd23a9146a753f4f9e10bf4cc99b53d040a5459c32f043965d75f0c2b4a78af6" +version = "0.7.0" +source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" dependencies = [ "futures-core", - "http-body", "pin-project-lite", "serde", "serde_json", "serde_urlencoded", "tokio", - "tower-layer", - "tower-service", "xitca-codegen", "xitca-http", "xitca-server", "xitca-service", "xitca-unsafe-collection", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/frameworks/Rust/xitca-web/Cargo.toml b/frameworks/Rust/xitca-web/Cargo.toml index b9447c3e7ea..c547c16de48 100644 --- a/frameworks/Rust/xitca-web/Cargo.toml +++ b/frameworks/Rust/xitca-web/Cargo.toml @@ -9,9 +9,9 @@ path = "./src/main.rs" required-features = ["io-uring", "pg", "router", "template"] [[bin]] -name = "xitca-web-iou" -path = "./src/main_iou.rs" -required-features = ["io-uring", "perf", "pg-iou", "template"] +name = "xitca-web-unrealistic" +path = "./src/main_unrealistic.rs" +required-features = ["perf", "pg", "template"] [[bin]] name = "xitca-web-wasm" @@ -19,9 +19,9 @@ path = "./src/main_wasm.rs" required-features = ["web"] [[bin]] -name = "xitca-web-axum" -path = "./src/main_axum.rs" -required-features = ["axum", "io-uring", "perf", "pg-sync", "template"] +name = "xitca-web-orm" +path = "./src/main_orm.rs" +required-features = ["pg-orm-async", "template", "web-codegen"] [[bin]] name = "xitca-web-sync" @@ -29,57 +29,56 @@ path = "./src/main_sync.rs" required-features = ["pg-orm", "template", "web-codegen"] [features] -# pg optional -pg = ["xitca-postgres/single-thread"] -# pg send/sync optional -pg-sync = ["xitca-postgres"] -# pg io_uring optional -pg-iou = ["pg", "xitca-postgres/io-uring"] -# pg orm optional -pg-orm = ["diesel"] +# pg client optional +pg = ["dep:xitca-postgres"] +# diesel orm optional +pg-orm = ["diesel/r2d2"] +# diesel async orm optional +pg-orm-async = ["dep:diesel", "dep:diesel-async", "dep:xitca-postgres-diesel", "dep:futures-util"] # http router optional router = ["xitca-http/router"] # web optional -web = ["xitca-web"] -# web codegen optional +web = ["dep:xitca-web"] +# web with macros optional web-codegen = ["xitca-web/codegen", "xitca-web/urlencoded"] # template optional -template = ["sailfish"] +template = ["dep:sailfish"] # io-uring optional -io-uring = ["xitca-http/io-uring", "xitca-server/io-uring"] -# axum optional -axum = ["dep:axum", "http-body", "tower", "tower-http", "xitca-web/tower-http-compat" ] +io-uring = ["dep:tokio-uring", "xitca-http/io-uring", "xitca-server/io-uring"] # unrealistic performance optimization -perf = ["mimalloc", "tokio/parking_lot"] +perf = ["dep:mimalloc", "tokio/parking_lot"] [dependencies] -xitca-http = "0.5" -xitca-io = "0.3" -xitca-server = "0.3" -xitca-service = "0.1" -xitca-unsafe-collection = "0.1.1" +xitca-http = "0.7" +xitca-io = "0.4.1" +xitca-server = "0.5" +xitca-service = "0.3" +xitca-unsafe-collection = "0.2" atoi = "2" +httparse = "1" serde = { version = "1" } serde_json = { version = "1" } # web optional -xitca-web = { version = "0.5", features = ["json"], optional = true } +xitca-web = { version = "0.7", features = ["json"], optional = true } # raw-pg optional -xitca-postgres = { version = "0.1", optional = true } +xitca-postgres = { version = "0.3", optional = true } # orm optional -diesel = { version = "2", features = ["postgres", "r2d2"], optional = true } +diesel = { version = "2", features = ["postgres"], optional = true } + +# orm async optional +diesel-async = { version = "0.5", features = ["bb8", "postgres"], optional = true } +xitca-postgres-diesel = { version = "0.1", optional = true } +futures-util = { version = "0.3", default-features = false, optional = true } # template optional -sailfish = { version = "0.8", default-features = false, features = ["derive", "perf-inline"], optional = true } +sailfish = { version = "0.9", default-features = false, features = ["perf-inline"], optional = true } -# axum optional -axum = { version = "0.7", optional = true, default-features = false, features = ["json", "query"] } -http-body = { version = "1", optional = true } -tower = { version = "0.4", optional = true } -tower-http = { version = "0.5", features = ["set-header"], optional = true } +# io-uring optional +tokio-uring = { version = "0.5", optional = true } # perf optional mimalloc = { version = "0.1", default-features = false, optional = true } @@ -87,7 +86,7 @@ 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 } -nanorand = { version = "0.7", default-features = false, features = ["tls"] } +rand = { version = "0.8", features = ["small_rng"] } tokio = "1" [profile.release] @@ -97,4 +96,15 @@ codegen-units = 1 panic = "abort" [patch.crates-io] -xitca-postgres = { git = "https://github.com/HFQR/xitca-web.git", rev = "ea1f5a2447e0969a6dff84eac9ff9ff90dbc7ed1" } +xitca-postgres-diesel = { git = "https://github.com/fakeshadow/xitca-postgres-diesel", rev = "ae93ee9" } + +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" } diff --git a/frameworks/Rust/xitca-web/benchmark_config.json b/frameworks/Rust/xitca-web/benchmark_config.json index 5371bcd2551..db81e362742 100755 --- a/frameworks/Rust/xitca-web/benchmark_config.json +++ b/frameworks/Rust/xitca-web/benchmark_config.json @@ -24,7 +24,7 @@ "notes": "", "versus": "" }, - "iou": { + "unrealistic": { "json_url": "/json", "plaintext_url": "/plaintext", "db_url": "/db", @@ -35,7 +35,7 @@ "approach": "Stripped", "classification": "Platform", "database": "Postgres", - "framework": "xitca-web [unrealistic]", + "framework": "xitca-web", "language": "Rust", "orm": "Raw", "platform": "None", @@ -53,7 +53,7 @@ "approach": "Realistic", "classification": "Micro", "database": "none", - "framework": "xitca-web [wasm]", + "framework": "xitca-web", "language": "rust", "orm": "raw", "platform": "none", @@ -62,10 +62,9 @@ "database_os": "linux", "display_name": "xitca-web [wasm]", "notes": "", - "versus": "", - "tags": ["broken"] + "versus": "" }, - "axum": { + "orm": { "json_url": "/json", "plaintext_url": "/plaintext", "db_url": "/db", @@ -74,16 +73,16 @@ "update_url": "/updates?q=", "port": 8080, "approach": "realistic", - "classification": "micro", + "classification": "fullstack", "database": "postgres", - "framework": "axum [xitca]", + "framework": "xitca-web", "language": "rust", - "orm": "raw", + "orm": "full", "platform": "none", "webserver": "xitca-server", "os": "linux", "database_os": "linux", - "display_name": "axum [xitca]", + "display_name": "xitca-web [orm]", "notes": "", "versus": "" }, @@ -98,7 +97,7 @@ "approach": "realistic", "classification": "micro", "database": "postgres", - "framework": "xitca-web [sync]", + "framework": "xitca-web", "language": "rust", "orm": "full", "platform": "none", diff --git a/frameworks/Rust/xitca-web/rustfmt.toml b/frameworks/Rust/xitca-web/rustfmt.toml new file mode 100644 index 00000000000..866c7561055 --- /dev/null +++ b/frameworks/Rust/xitca-web/rustfmt.toml @@ -0,0 +1 @@ +max_width = 120 \ No newline at end of file diff --git a/frameworks/Rust/xitca-web/src/db.rs b/frameworks/Rust/xitca-web/src/db.rs index 755dfaacf70..abb9ef10870 100644 --- a/frameworks/Rust/xitca-web/src/db.rs +++ b/frameworks/Rust/xitca-web/src/db.rs @@ -1,107 +1,61 @@ -use std::{collections::HashMap, fmt::Write}; +#[path = "./db_util.rs"] +mod db_util; -use xitca_postgres::{ - pipeline::Pipeline, statement::Statement, AsyncLendingIterator, SharedClient, -}; -use xitca_unsafe_collection::no_hash::NoHashBuilder; +use core::cell::RefCell; + +use xitca_postgres::{iter::AsyncLendingIterator, pipeline::Pipeline, pool::Pool, statement::Statement, Execute}; use super::{ ser::{Fortune, Fortunes, World}, - util::{HandleResult, Rand, DB_URL}, + util::{HandleResult, DB_URL}, }; +use db_util::{not_found, sort_update_params, update_query_from_num, Shared, FORTUNE_STMT, WORLD_STMT}; + pub struct Client { - client: SharedClient, - #[cfg(not(feature = "pg-sync"))] - rng: std::cell::RefCell, - #[cfg(feature = "pg-sync")] - rng: std::sync::Mutex, - fortune: Statement, - world: Statement, - updates: HashMap, + pool: Pool, + shared: RefCell, + updates: Box<[Box]>, } pub async fn create() -> HandleResult { - let mut client = SharedClient::new(DB_URL.to_string()).await?; - - let fortune = client.prepare_cached("SELECT * FROM fortune", &[]).await?; - - let world = client - .prepare_cached("SELECT * FROM world WHERE id=$1", &[]) - .await?; - - let mut updates = HashMap::default(); - - for num in 1..=500u16 { - let mut pl = 1; - let mut q = String::new(); - q.push_str("UPDATE world SET randomnumber = CASE id "); - for _ in 1..=num { - let _ = write!(&mut q, "when ${} then ${} ", pl, pl + 1); - pl += 2; - } - q.push_str("ELSE randomnumber END WHERE id IN ("); - for _ in 1..=num { - let _ = write!(&mut q, "${},", pl); - pl += 1; - } - q.pop(); - q.push(')'); - - let st = client.prepare_cached(&q, &[]).await?; - updates.insert(num, st); - } - Ok(Client { - client, - #[cfg(not(feature = "pg-sync"))] - rng: std::cell::RefCell::new(Rand::default()), - #[cfg(feature = "pg-sync")] - rng: std::sync::Mutex::new(Rand::default()), - fortune, - world, - updates, + pool: Pool::builder(DB_URL).capacity(1).build()?, + shared: Default::default(), + updates: core::iter::once(Box::from("")) + .chain((1..=500).map(update_query_from_num)) + .collect(), }) } impl Client { - #[cfg(not(feature = "pg-sync"))] - fn borrow_rng_mut(&self) -> std::cell::RefMut<'_, Rand> { - self.rng.borrow_mut() - } - - #[cfg(feature = "pg-sync")] - fn borrow_rng_mut(&self) -> std::sync::MutexGuard<'_, Rand> { - self.rng.lock().unwrap() - } - pub async fn get_world(&self) -> HandleResult { - let id = self.borrow_rng_mut().gen_id(); - self.client - .query_raw(&self.world, [id]) - .await? - .try_next() - .await? - .map(|row| World::new(row.get_raw(0), row.get_raw(1))) - .ok_or_else(|| "World does not exist".into()) + let mut conn = self.pool.get().await?; + let stmt = WORLD_STMT.execute(&mut conn).await?; + let id = self.shared.borrow_mut().0.gen_id(); + let mut res = stmt.bind([id]).query(&conn.consume()).await?; + let row = res.try_next().await?.ok_or_else(not_found)?; + Ok(World::new(row.get(0), row.get(1))) } pub async fn get_worlds(&self, num: u16) -> HandleResult> { - let mut pipe = Pipeline::new(); + let len = num as usize; - { - let mut rng = self.borrow_rng_mut(); - (0..num).try_for_each(|_| pipe.query_raw(&self.world, [rng.gen_id()]))?; - } + let mut conn = self.pool.get().await?; + let stmt = WORLD_STMT.execute(&mut conn).await?; - let mut worlds = Vec::new(); - worlds.reserve(num as usize); + let mut res = { + let (ref mut rng, ref mut buf) = *self.shared.borrow_mut(); + let mut pipe = Pipeline::with_capacity_from_buf(len, buf); + (0..num).try_for_each(|_| stmt.bind([rng.gen_id()]).query(&mut pipe))?; + pipe.query(&conn.consume())? + }; + + let mut worlds = Vec::with_capacity(len); - let mut res = self.client.pipeline(pipe).await?; while let Some(mut item) = res.try_next().await? { - while let Some(row) = item.try_next().await? { - worlds.push(World::new(row.get_raw(0), row.get_raw(1))) - } + let row = item.try_next().await?.ok_or_else(not_found)?; + worlds.push(World::new(row.get(0), row.get(1))); } Ok(worlds) @@ -110,34 +64,34 @@ impl Client { pub async fn update(&self, num: u16) -> HandleResult> { let len = num as usize; - let mut params = Vec::new(); - params.reserve(len * 3); + let update = self.updates.get(len).ok_or("request num is out of range")?; + let mut conn = self.pool.get().await?; + let world_stmt = WORLD_STMT.execute(&mut conn).await?; + let update_stmt = Statement::named(update, &[]).execute(&mut conn).await?; - let mut pipe = Pipeline::new(); + let mut params = Vec::with_capacity(len); - { - let mut rng = self.borrow_rng_mut(); + let mut res = { + let (ref mut rng, ref mut buf) = *self.shared.borrow_mut(); + let mut pipe = Pipeline::with_capacity_from_buf(len + 1, buf); (0..num).try_for_each(|_| { let w_id = rng.gen_id(); let r_id = rng.gen_id(); - params.extend([w_id, r_id]); - pipe.query_raw(&self.world, [w_id]) + params.push([w_id, r_id]); + world_stmt.bind([w_id]).query(&mut pipe) })?; - } + update_stmt.bind(sort_update_params(¶ms)).query(&mut pipe)?; + pipe.query(&conn.consume())? + }; - params.extend_from_within(..len); - let st = self.updates.get(&num).unwrap(); - pipe.query_raw(st, ¶ms)?; + let mut worlds = Vec::with_capacity(len); - let mut worlds = Vec::new(); - worlds.reserve(len); - let mut r_ids = params.into_iter().skip(1).step_by(2); + let mut r_ids = params.into_iter(); - let mut res = self.client.pipeline(pipe).await?; while let Some(mut item) = res.try_next().await? { while let Some(row) = item.try_next().await? { - let r_id = r_ids.next().unwrap(); - worlds.push(World::new(row.get_raw(0), r_id)) + let r_id = r_ids.next().unwrap()[1]; + worlds.push(World::new(row.get(0), r_id)) } } @@ -148,10 +102,14 @@ impl Client { let mut items = Vec::with_capacity(32); items.push(Fortune::new(0, "Additional fortune added at request time.")); - let mut stream = self.client.query_raw::<[i32; 0]>(&self.fortune, []).await?; - while let Some(row) = stream.try_next().await? { - items.push(Fortune::new(row.get_raw(0), row.get_raw::(1))); + let mut conn = self.pool.get().await?; + let stmt = FORTUNE_STMT.execute(&mut conn).await?; + let mut res = stmt.query(&conn.consume()).await?; + + while let Some(row) = res.try_next().await? { + items.push(Fortune::new(row.get(0), row.get::(1))); } + items.sort_by(|it, next| it.message.cmp(&next.message)); Ok(Fortunes::new(items)) diff --git a/frameworks/Rust/xitca-web/src/db_diesel.rs b/frameworks/Rust/xitca-web/src/db_diesel.rs index 49a3e158e18..1675a7c2cd1 100644 --- a/frameworks/Rust/xitca-web/src/db_diesel.rs +++ b/frameworks/Rust/xitca-web/src/db_diesel.rs @@ -1,12 +1,20 @@ -use std::sync::{Arc, Mutex}; +#[path = "./db_util.rs"] +mod db_util; + +use std::{ + io, + sync::{Arc, Mutex}, +}; use diesel::{prelude::*, r2d2}; use crate::{ ser::{Fortune, Fortunes, World}, - util::{Error, HandleResult, Rand, DB_URL}, + util::{HandleResult, Rand, DB_URL}, }; +use db_util::{not_found, update_query_from_ids}; + pub type Pool = Arc<_Pool>; pub struct _Pool { @@ -14,15 +22,15 @@ pub struct _Pool { rng: Mutex, } -pub fn create() -> std::io::Result> { +pub fn create() -> io::Result> { r2d2::Builder::new() - .max_size(256) - .min_idle(Some(256)) + .max_size(100) + .min_idle(Some(100)) .test_on_check_out(false) .idle_timeout(None) .max_lifetime(None) .build(r2d2::ConnectionManager::new(DB_URL)) - .map_err(std::io::Error::other) + .map_err(io::Error::other) .map(|pool| { Arc::new(_Pool { pool, @@ -31,76 +39,51 @@ pub fn create() -> std::io::Result> { }) } -#[cold] -#[inline(never)] -fn not_found() -> Error { - format!("world not found").into() -} - impl _Pool { pub fn get_world(&self) -> HandleResult { use crate::schema::world::dsl::*; let w_id = self.rng.lock().unwrap().gen_id(); let mut conn = self.pool.get()?; - world - .filter(id.eq(w_id)) - .load(&mut conn)? - .pop() - .ok_or_else(not_found) + world.filter(id.eq(w_id)).load(&mut conn)?.pop().ok_or_else(not_found) } pub fn get_worlds(&self, num: u16) -> HandleResult> { use crate::schema::world::dsl::*; let mut conn = self.pool.get()?; - (0..num) - .map(|_| { - let w_id = self.rng.lock().unwrap().gen_id(); - world - .filter(id.eq(w_id)) - .load::(&mut conn)? - .pop() - .ok_or_else(not_found) - }) - .collect() + core::iter::repeat_with(|| { + let w_id = self.rng.lock().unwrap().gen_id(); + world.filter(id.eq(w_id)).load(&mut conn)?.pop().ok_or_else(not_found) + }) + .take(num as _) + .collect() } pub fn update(&self, num: u16) -> HandleResult> { use crate::schema::world::dsl::*; - let mut worlds = { - let mut conn = self.pool.get()?; - let worlds = (0..num) - .map(|_| { - let mut rng = self.rng.lock().unwrap(); - let w_id = rng.gen_id(); - let r_id = rng.gen_id(); - drop(rng); - world - .filter(id.eq(w_id)) - .load::(&mut conn)? - .pop() - .map(|mut w| { - w.randomnumber = r_id; - w - }) - .ok_or_else(not_found) - }) - .collect::>>()?; - - worlds.iter().try_for_each(|w| { - diesel::update(world) - .filter(id.eq(w.id)) - .set(randomnumber.eq(w.randomnumber)) - .execute(&mut conn) - .map(|_| ()) - })?; - - worlds + let mut rngs = { + let mut rng = self.rng.lock().unwrap(); + (0..num).map(|_| (rng.gen_id(), rng.gen_id())).collect::>() }; - worlds.sort_by_key(|w| w.id); + rngs.sort_by(|(a, _), (b, _)| a.cmp(b)); + + let update_sql = update_query_from_ids(&rngs); + + let mut conn = self.pool.get()?; + + let worlds = rngs + .into_iter() + .map(|(w_id, num)| { + let mut w: World = world.filter(id.eq(w_id)).load(&mut conn)?.pop().ok_or_else(not_found)?; + w.randomnumber = num; + Ok(w) + }) + .collect::>>()?; + + diesel::sql_query(update_sql).execute(&mut conn)?; Ok(worlds) } @@ -110,7 +93,7 @@ impl _Pool { let mut items = { let mut conn = self.pool.get()?; - fortune.load::(&mut conn)? + fortune.load(&mut conn)? }; items.push(Fortune::new(0, "Additional fortune added at request time.")); diff --git a/frameworks/Rust/xitca-web/src/db_diesel_async.rs b/frameworks/Rust/xitca-web/src/db_diesel_async.rs new file mode 100644 index 00000000000..e3ee7e895fd --- /dev/null +++ b/frameworks/Rust/xitca-web/src/db_diesel_async.rs @@ -0,0 +1,122 @@ +#[path = "./db_util.rs"] +mod db_util; + +use std::{io, sync::Mutex}; + +use diesel::prelude::*; +use diesel_async::{ + pooled_connection::{bb8, AsyncDieselConnectionManager}, + RunQueryDsl, +}; +use futures_util::{ + future::join, + stream::{FuturesUnordered, TryStreamExt}, +}; +use xitca_postgres_diesel::AsyncPgConnection; + +use crate::{ + ser::{Fortune, Fortunes, World}, + util::{HandleResult, Rand, DB_URL}, +}; + +use db_util::{not_found, update_query_from_ids}; + +pub struct Pool { + pool: bb8::Pool, + rng: Mutex, +} + +pub async fn create() -> io::Result { + bb8::Pool::builder() + .max_size(1) + .min_idle(Some(1)) + .test_on_check_out(false) + .build(AsyncDieselConnectionManager::new(DB_URL)) + .await + .map_err(io::Error::other) + .map(|pool| Pool { + pool, + rng: Mutex::new(Rand::default()), + }) +} + +impl Pool { + pub async fn get_world(&self) -> HandleResult { + use crate::schema::world::dsl::*; + { + let w_id = self.rng.lock().unwrap().gen_id(); + let mut conn = self.pool.get().await?; + world.filter(id.eq(w_id)).load(&mut conn) + } + .await? + .pop() + .ok_or_else(not_found) + } + + pub async fn get_worlds(&self, num: u16) -> HandleResult> { + use crate::schema::world::dsl::*; + { + let mut conn = self.pool.get().await?; + let mut rng = self.rng.lock().unwrap(); + core::iter::repeat_with(|| { + let w_id = rng.gen_id(); + let fut = world.filter(id.eq(w_id)).load(&mut conn); + async { fut.await?.pop().ok_or_else(not_found) } + }) + .take(num as _) + .collect::>() + } + .try_collect() + .await + } + + pub async fn update(&self, num: u16) -> HandleResult> { + use crate::schema::world::dsl::*; + + let (select_res, update_res) = { + let mut conn = self.pool.get().await?; + let mut rng = self.rng.lock().unwrap(); + + let (select, mut rngs) = core::iter::repeat_with(|| { + let w_id = rng.gen_id(); + let num = rng.gen_id(); + + let fut = world.filter(id.eq(w_id)).load::(&mut conn); + let select = async move { + let mut w = fut.await?.pop().ok_or_else(not_found)?; + w.randomnumber = num; + HandleResult::Ok(w) + }; + + (select, (w_id, num)) + }) + .take(num as _) + .collect::<(FuturesUnordered<_>, Vec<_>)>(); + + rngs.sort_by(|(a, _), (b, _)| a.cmp(b)); + + let update = diesel::sql_query(update_query_from_ids(&rngs)).execute(&mut conn); + + join(select.try_collect::>(), update) + } + .await; + + update_res?; + select_res + } + + pub async fn tell_fortune(&self) -> HandleResult { + use crate::schema::fortune::dsl::*; + + let mut items = { + let mut conn = self.pool.get().await?; + fortune.load(&mut conn) + } + .await?; + + items.push(Fortune::new(0, "Additional fortune added at request time.")); + items.sort_by(|it, next| it.message.cmp(&next.message)); + + Ok(Fortunes::new(items)) + } +} diff --git a/frameworks/Rust/xitca-web/src/db_unrealistic.rs b/frameworks/Rust/xitca-web/src/db_unrealistic.rs new file mode 100644 index 00000000000..99e4249e39b --- /dev/null +++ b/frameworks/Rust/xitca-web/src/db_unrealistic.rs @@ -0,0 +1,131 @@ +//! this module is unrealistic. related issue: +//! https://github.com/TechEmpower/FrameworkBenchmarks/issues/8790 + +#[path = "./db_util.rs"] +mod db_util; + +use std::cell::RefCell; + +use xitca_postgres::{iter::AsyncLendingIterator, pipeline::Pipeline, statement::Statement, Execute}; + +use super::{ + ser::{Fortune, Fortunes, World}, + util::{HandleResult, DB_URL}, +}; + +use db_util::{not_found, sort_update_params, update_query_from_num, Shared, FORTUNE_STMT, WORLD_STMT}; + +pub struct Client { + cli: xitca_postgres::Client, + shared: RefCell, + fortune: Statement, + world: Statement, + updates: Box<[Statement]>, +} + +pub async fn create() -> HandleResult { + let (cli, mut drv) = xitca_postgres::Postgres::new(DB_URL).connect().await?; + + tokio::task::spawn(tokio::task::unconstrained(async move { + while drv.try_next().await?.is_some() {} + HandleResult::Ok(()) + })); + + let world = WORLD_STMT.execute(&cli).await?.leak(); + let fortune = FORTUNE_STMT.execute(&cli).await?.leak(); + + let mut updates = vec![Statement::default()]; + + for update in (1..=500).map(update_query_from_num).into_iter() { + let stmt = Statement::named(&update, &[]).execute(&cli).await?.leak(); + updates.push(stmt); + } + + Ok(Client { + cli, + shared: Default::default(), + world, + fortune, + updates: updates.into_boxed_slice(), + }) +} + +impl Client { + pub async fn get_world(&self) -> HandleResult { + let id = self.shared.borrow_mut().0.gen_id(); + let mut res = self.world.bind([id]).query(&self.cli).await?; + let row = res.try_next().await?.ok_or_else(not_found)?; + Ok(World::new(row.get(0), row.get(1))) + } + + pub async fn get_worlds(&self, num: u16) -> HandleResult> { + let len = num as usize; + + let mut res = Vec::with_capacity(len); + + { + let (ref mut rng, ..) = *self.shared.borrow_mut(); + for _ in 0..len { + let stream = self.world.bind([rng.gen_id()]).query(&self.cli).await?; + res.push(stream); + } + }; + + let mut worlds = Vec::with_capacity(len); + + for mut stream in res { + let row = stream.try_next().await?.ok_or_else(not_found)?; + worlds.push(World::new(row.get(0), row.get(1))); + } + + Ok(worlds) + } + + pub async fn update(&self, num: u16) -> HandleResult> { + let len = num as usize; + + let mut params = Vec::with_capacity(len); + + let mut res = { + let (ref mut rng, ref mut buf) = *self.shared.borrow_mut(); + // unrealistic as all queries are sent with only one sync point. + let mut pipe = Pipeline::unsync_with_capacity_from_buf(len + 1, buf); + (0..num).try_for_each(|_| { + let w_id = rng.gen_id(); + let r_id = rng.gen_id(); + params.push([w_id, r_id]); + self.world.bind([w_id]).query(&mut pipe) + })?; + self.updates[len].bind(sort_update_params(¶ms)).query(&mut pipe)?; + pipe.query(&self.cli)? + }; + + let mut worlds = Vec::with_capacity(len); + + let mut r_ids = params.into_iter(); + + while let Some(mut item) = res.try_next().await? { + while let Some(row) = item.try_next().await? { + let r_id = r_ids.next().unwrap()[1]; + worlds.push(World::new(row.get(0), r_id)) + } + } + + Ok(worlds) + } + + pub async fn tell_fortune(&self) -> HandleResult { + let mut items = Vec::with_capacity(32); + items.push(Fortune::new(0, "Additional fortune added at request time.")); + + let mut res = self.fortune.query(&self.cli).await?; + + while let Some(row) = res.try_next().await? { + items.push(Fortune::new(row.get(0), row.get::(1))); + } + + items.sort_by(|it, next| it.message.cmp(&next.message)); + + Ok(Fortunes::new(items)) + } +} diff --git a/frameworks/Rust/xitca-web/src/db_util.rs b/frameworks/Rust/xitca-web/src/db_util.rs new file mode 100644 index 00000000000..42c2c455bbe --- /dev/null +++ b/frameworks/Rust/xitca-web/src/db_util.rs @@ -0,0 +1,96 @@ +use crate::util::Error; + +#[cfg(any(feature = "pg-orm", feature = "pg-orm-async"))] +// diesel does not support high level bulk update api. use raw sql to bypass the limitation. +// relate discussion: https://github.com/diesel-rs/diesel/discussions/2879 +pub fn update_query_from_ids(ids: &[(i32, i32)]) -> String { + update_query(|query| { + use core::fmt::Write; + ids.iter().for_each(|(w_id, num)| { + write!(query, "({}::int,{}::int),", w_id, num).unwrap(); + }); + }) +} + +fn update_query(func: impl FnOnce(&mut String)) -> String { + const PREFIX: &str = "UPDATE world SET randomNumber = w.r FROM (VALUES "; + const SUFFIX: &str = ") AS w (i,r) WHERE world.id = w.i"; + + let mut query = String::from(PREFIX); + + func(&mut query); + + if query.ends_with(',') { + query.pop(); + } + + query.push_str(SUFFIX); + + query +} + +#[cold] +#[inline(never)] +pub fn not_found() -> Error { + "request World does not exist".into() +} + +#[cfg(feature = "pg")] +pub use pg::*; + +#[cfg(feature = "pg")] +pub mod pg { + use xitca_io::bytes::BytesMut; + use xitca_postgres::{ + statement::{Statement, StatementNamed}, + types::Type, + }; + + use crate::util::Rand; + + pub type Shared = (Rand, BytesMut); + + pub const FORTUNE_STMT: StatementNamed = Statement::named("SELECT * FROM fortune", &[]); + pub const WORLD_STMT: StatementNamed = Statement::named("SELECT * FROM world WHERE id=$1", &[Type::INT4]); + + pub fn update_query_from_num(num: usize) -> Box { + super::update_query(|query| { + use core::fmt::Write; + (1..=num).fold(1, |idx, _| { + write!(query, "(${}::int,${}::int),", idx, idx + 1).unwrap(); + idx + 2 + }); + }) + .into_boxed_str() + } + + pub fn sort_update_params(params: &[[i32; 2]]) -> impl ExactSizeIterator { + let mut params = params.to_owned(); + params.sort_by(|a, b| a[0].cmp(&b[0])); + + struct ParamIter(I); + + impl Iterator for ParamIter + where + I: Iterator, + { + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + } + + // impl depends on compiler optimization to flat Vec<[T]> to Vec when inferring + // it's size hint. possible to cause runtime panic. + impl ExactSizeIterator for ParamIter where I: Iterator {} + + ParamIter(params.into_iter().flatten()) + } +} diff --git a/frameworks/Rust/xitca-web/src/main.rs b/frameworks/Rust/xitca-web/src/main.rs index 632504dac5e..b7389fbb821 100755 --- a/frameworks/Rust/xitca-web/src/main.rs +++ b/frameworks/Rust/xitca-web/src/main.rs @@ -3,26 +3,24 @@ mod ser; mod util; use xitca_http::{ - body::Once, - bytes::Bytes, h1::RequestBody, - http::{ - self, - const_header_value::{TEXT, TEXT_HTML_UTF8}, - header::{CONTENT_TYPE, SERVER}, - IntoResponse, RequestExt, + http::{header::SERVER, StatusCode}, + util::{ + middleware::context::{Context, ContextBuilder}, + service::{ + route::get, + router::{Router, RouterError}, + }, }, - util::service::{route::get, router::Router}, HttpServiceBuilder, }; use xitca_service::{fn_service, Service, ServiceExt}; -use ser::{json_response, Message}; -use util::{context_mw, HandleResult, QueryParse, SERVER_HEADER_VALUE}; +use db::Client; +use ser::{error_response, IntoResponse, Message, Request, Response}; +use util::{HandleResult, QueryParse, State, SERVER_HEADER_VALUE}; -type Request = http::Request>; -type Response = http::Response>; -type Ctx<'a> = util::Ctx<'a, Request>; +type Ctx<'a> = Context<'a, Request, State>; fn main() -> std::io::Result<()> { let service = Router::new() @@ -32,8 +30,8 @@ fn main() -> std::io::Result<()> { .insert("/fortunes", get(fn_service(fortunes))) .insert("/queries", get(fn_service(queries))) .insert("/updates", get(fn_service(updates))) - .enclosed_fn(middleware_fn) - .enclosed(context_mw()) + .enclosed_fn(middleware) + .enclosed(ContextBuilder::new(|| async { db::create().await.map(State::new) })) .enclosed(HttpServiceBuilder::h1().io_uring()); xitca_server::Builder::new() .bind("xitca-web", "0.0.0.0:8080", service)? @@ -41,53 +39,60 @@ fn main() -> std::io::Result<()> { .wait() } -async fn middleware_fn(service: &S, req: Ctx<'_>) -> Result +async fn middleware(service: &S, req: Ctx<'_>) -> Result where - S: for<'c> Service, Response = Response, Error = E>, + S: for<'c> Service, Response = Response, Error = RouterError>, { - service.call(req).await.map(|mut res| { - res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE); - res - }) + let mut res = service.call(req).await.unwrap_or_else(error_handler); + res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE); + Ok(res) +} + +#[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) + } + } } async fn plain_text(ctx: Ctx<'_>) -> HandleResult { - let (req, _) = ctx.into_parts(); - let mut res = req.into_response(Bytes::from_static(b"Hello, World!")); - res.headers_mut().insert(CONTENT_TYPE, TEXT); - Ok(res) + ctx.into_parts().0.text_response() } async fn json(ctx: Ctx<'_>) -> HandleResult { let (req, state) = ctx.into_parts(); - json_response(req, &mut state.write_buf.borrow_mut(), &Message::new()) + req.json_response(state, &Message::new()) } async fn db(ctx: Ctx<'_>) -> HandleResult { let (req, state) = ctx.into_parts(); let world = state.client.get_world().await?; - json_response(req, &mut state.write_buf.borrow_mut(), &world) + req.json_response(state, &world) } async fn fortunes(ctx: Ctx<'_>) -> HandleResult { let (req, state) = ctx.into_parts(); use sailfish::TemplateOnce; let fortunes = state.client.tell_fortune().await?.render_once()?; - let mut res = req.into_response(Bytes::from(fortunes)); - res.headers_mut().insert(CONTENT_TYPE, TEXT_HTML_UTF8); - Ok(res) + req.html_response(fortunes) } async fn queries(ctx: Ctx<'_>) -> HandleResult { let (req, state) = ctx.into_parts(); let num = req.uri().query().parse_query(); let worlds = state.client.get_worlds(num).await?; - json_response(req, &mut state.write_buf.borrow_mut(), worlds.as_slice()) + req.json_response(state, &worlds) } async fn updates(ctx: Ctx<'_>) -> HandleResult { let (req, state) = ctx.into_parts(); let num = req.uri().query().parse_query(); let worlds = state.client.update(num).await?; - json_response(req, &mut state.write_buf.borrow_mut(), worlds.as_slice()) + req.json_response(state, &worlds) } diff --git a/frameworks/Rust/xitca-web/src/main_axum.rs b/frameworks/Rust/xitca-web/src/main_axum.rs deleted file mode 100644 index d15aaefdc6e..00000000000 --- a/frameworks/Rust/xitca-web/src/main_axum.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! show case of axum running on proper thread per core server with io-uring enabled. - -#[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; - -mod db; -mod ser; -mod util; - -use std::sync::Arc; - -use axum::{ - extract::{Json, Query, State}, - http::{ - header::{HeaderValue, SERVER}, - StatusCode, - }, - response::{Html, IntoResponse, Response}, - routing::{get, Router}, -}; -use tower_http::set_header::SetResponseHeaderLayer; - -use crate::{db::Client, ser::Num, tower_compat::TowerHttp}; - -fn main() -> std::io::Result<()> { - let service = TowerHttp::service(|| async { - let cli = db::create().await?; - let service = Router::new() - .route("/plaintext", get(plain_text)) - .route("/json", get(json)) - .route("/db", get(db)) - .route("/fortunes", get(fortunes)) - .route("/queries", get(queries)) - .route("/updates", get(updates)) - .with_state(Arc::new(cli)) - .layer(SetResponseHeaderLayer::if_not_present( - SERVER, - HeaderValue::from_static("A"), - )); - Ok(service) - }); - xitca_server::Builder::new() - .bind("xitca-axum", "0.0.0.0:8080", service)? - .build() - .wait() -} - -async fn plain_text() -> &'static str { - "Hello, World!" -} - -async fn json() -> impl IntoResponse { - Json(ser::Message::new()) -} - -async fn db(State(cli): State>) -> impl IntoResponse { - cli.get_world().await.map(Json).map_err(Error) -} - -async fn fortunes(State(cli): State>) -> impl IntoResponse { - use sailfish::TemplateOnce; - cli.tell_fortune() - .await - .map_err(Error)? - .render_once() - .map(Html) - .map_err(|e| Error(Box::new(e))) -} - -async fn queries(State(cli): State>, Query(Num(num)): Query) -> impl IntoResponse { - cli.get_worlds(num).await.map(Json).map_err(Error) -} - -async fn updates(State(cli): State>, Query(Num(num)): Query) -> impl IntoResponse { - cli.update(num).await.map(Json).map_err(Error) -} - -struct Error(util::Error); - -impl IntoResponse for Error { - fn into_response(self) -> Response { - let mut res = self.0.to_string().into_response(); - *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - res - } -} - -// compat module between xitca-http and axum. -mod tower_compat { - use core::{cell::RefCell, fmt, future::Future, marker::PhantomData}; - - use std::net::SocketAddr; - - use http_body::Body; - use xitca_http::{ - bytes::Bytes, - h1::RequestBody, - http::{Request, RequestExt, Response}, - HttpServiceBuilder, - }; - use xitca_io::net::io_uring::TcpStream; - use xitca_service::{ - fn_build, middleware::UncheckedReady, ready::ReadyService, Service, ServiceExt, - }; - use xitca_web::service::tower_http_compat::{CompatReqBody, CompatResBody}; - - pub struct TowerHttp { - service: RefCell, - _p: PhantomData, - } - - impl TowerHttp { - pub fn service( - func: F, - ) -> impl Service< - Response = impl ReadyService + Service<(TcpStream, SocketAddr)>, - Error = impl fmt::Debug, - > - where - F: Fn() -> Fut + Send + Sync + Clone, - Fut: Future>, - S: tower::Service< - Request, ()>>, - Response = Response, - >, - S::Error: fmt::Debug, - B: Body + Send + 'static, - { - fn_build(move |_| { - let func = func.clone(); - async move { - func().await.map(|service| TowerHttp { - service: RefCell::new(service), - _p: PhantomData, - }) - } - }) - .enclosed(UncheckedReady) - .enclosed(HttpServiceBuilder::h1().io_uring()) - } - } - - impl Service>> for TowerHttp - where - S: tower::Service< - Request, ()>>, - Response = Response, - >, - { - type Response = Response>; - type Error = S::Error; - - async fn call( - &self, - req: Request>, - ) -> Result { - let (parts, ext) = req.into_parts(); - let req = Request::from_parts(parts, CompatReqBody::new(ext, ())); - let fut = self.service.borrow_mut().call(req); - let (parts, body) = fut.await?.into_parts(); - Ok(Response::from_parts(parts, CompatResBody::new(body))) - } - } -} diff --git a/frameworks/Rust/xitca-web/src/main_iou.rs b/frameworks/Rust/xitca-web/src/main_iou.rs deleted file mode 100644 index 83dfbc0eaab..00000000000 --- a/frameworks/Rust/xitca-web/src/main_iou.rs +++ /dev/null @@ -1,158 +0,0 @@ -// used as reference of if/how moving from epoll to io-uring(or mixture of the two) make sense for -// network io. - -#[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; - -mod db; -mod ser; -mod util; - -use std::{convert::Infallible, fmt, future::poll_fn, io, pin::pin}; - -use futures_core::stream::Stream; -use xitca_http::{ - body::Once, - date::DateTimeService, - h1::proto::context::Context, - http::{ - self, - const_header_value::{TEXT, TEXT_HTML_UTF8}, - header::{CONTENT_TYPE, SERVER}, - IntoResponse, RequestExt, StatusCode, - }, -}; -use xitca_io::{ - bytes::{Bytes, BytesMut}, - io_uring::IoBuf, - net::{io_uring::TcpStream as IOUTcpStream, TcpStream}, -}; -use xitca_service::{fn_build, fn_service, middleware::UncheckedReady, Service, ServiceExt}; - -use self::{ - ser::{json_response, Message}, - util::{context_mw, Ctx, QueryParse, SERVER_HEADER_VALUE}, -}; - -type Request = http::Request>; -type Response = http::Response>; - -fn main() -> io::Result<()> { - let service = fn_service(handler) - .enclosed(context_mw()) - .enclosed(fn_build(|res: Result<_, _>| async { - res.map(|service| Http1IOU { - service, - date: DateTimeService::new(), - }) - })) - .enclosed(UncheckedReady); - xitca_server::Builder::new() - .bind("xitca-iou", "0.0.0.0:8080", service)? - .build() - .wait() -} - -async fn handler(ctx: Ctx<'_, Request>) -> Result { - let (req, state) = ctx.into_parts(); - let mut res = match req.uri().path() { - "/plaintext" => { - const HELLO: Bytes = Bytes::from_static(b"Hello, World!"); - let mut res = req.into_response(HELLO); - res.headers_mut().insert(CONTENT_TYPE, TEXT); - res - } - "/json" => json_response(req, &mut state.write_buf.borrow_mut(), &Message::new()).unwrap(), - "/db" => { - let world = state.client.get_world().await.unwrap(); - json_response(req, &mut state.write_buf.borrow_mut(), &world).unwrap() - } - "/queries" => { - let num = req.uri().query().parse_query(); - let worlds = state.client.get_worlds(num).await.unwrap(); - json_response(req, &mut state.write_buf.borrow_mut(), worlds.as_slice()).unwrap() - } - "/updates" => { - let num = req.uri().query().parse_query(); - let worlds = state.client.update(num).await.unwrap(); - json_response(req, &mut state.write_buf.borrow_mut(), worlds.as_slice()).unwrap() - } - "/fortunes" => { - use sailfish::TemplateOnce; - let fortunes = state - .client - .tell_fortune() - .await - .unwrap() - .render_once() - .unwrap(); - let mut res = req.into_response(Bytes::from(fortunes)); - res.headers_mut().append(CONTENT_TYPE, TEXT_HTML_UTF8); - res - } - _ => { - let mut res = req.into_response(Bytes::new()); - *res.status_mut() = StatusCode::NOT_FOUND; - res - } - }; - res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE); - Ok(res) -} - -struct Http1IOU { - service: S, - date: DateTimeService, -} - -// runner for http service. -impl Service for Http1IOU -where - S: Service, - S::Error: fmt::Debug, -{ - type Response = (); - type Error = io::Error; - - async fn call(&self, stream: TcpStream) -> Result { - let std = stream.into_std()?; - let stream = IOUTcpStream::from_std(std); - - let mut ctx = Context::<_, 8>::new(self.date.get()); - let mut read_buf = BytesMut::new(); - let mut write_buf = BytesMut::with_capacity(4096); - - loop { - let len = read_buf.len(); - let rem = read_buf.capacity() - len; - if rem < 4096 { - read_buf.reserve(4096 - rem); - } - - let (res, buf) = stream.read(read_buf.slice(len..)).await; - read_buf = buf.into_inner(); - if res? == 0 { - break; - } - - while let Some((req, _)) = ctx.decode_head::<{ usize::MAX }>(&mut read_buf).unwrap() { - let (parts, body) = self.service.call(req).await.unwrap().into_parts(); - let mut encoder = ctx.encode_head(parts, &body, &mut write_buf).unwrap(); - let mut body = pin!(body); - let chunk = poll_fn(|cx| body.as_mut().poll_next(cx)) - .await - .unwrap() - .unwrap(); - encoder.encode(chunk, &mut write_buf); - encoder.encode_eof(&mut write_buf); - } - - let (res, b) = stream.write_all(write_buf).await; - write_buf = b; - write_buf.clear(); - res?; - } - - stream.shutdown(std::net::Shutdown::Both) - } -} diff --git a/frameworks/Rust/xitca-web/src/main_orm.rs b/frameworks/Rust/xitca-web/src/main_orm.rs new file mode 100644 index 00000000000..86e8037ac2e --- /dev/null +++ b/frameworks/Rust/xitca-web/src/main_orm.rs @@ -0,0 +1,67 @@ +mod db_diesel_async; +mod schema; +mod ser; +mod util; + +use serde::Serialize; +use xitca_web::{ + codegen::route, + handler::{html::Html, json::Json, query::Query, state::StateRef, text::Text}, + http::{header::SERVER, WebResponse}, + route::get, + App, +}; + +use db_diesel_async::Pool; +use ser::Num; +use util::{HandleResult, SERVER_HEADER_VALUE}; + +fn main() -> std::io::Result<()> { + App::new() + .with_async_state(db_diesel_async::create) + .at("/plaintext", get(Text("Hello, World!"))) + .at("/json", get(Json(ser::Message::new()))) + .at_typed(db) + .at_typed(fortunes) + .at_typed(queries) + .at_typed(updates) + .map(header) + .serve() + .disable_vectored_write() + .bind("0.0.0.0:8080")? + .run() + .wait() +} + +fn header(mut res: WebResponse) -> WebResponse { + res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE); + res +} + +#[route("/db", method = get)] +async fn db(StateRef(pool): StateRef<'_, Pool>) -> HandleResult> { + pool.get_world().await.map(Json) +} + +#[route("/fortunes", method = get)] +async fn fortunes(StateRef(pool): StateRef<'_, Pool>) -> HandleResult> { + use sailfish::TemplateOnce; + let html = pool.tell_fortune().await?.render_once()?; + Ok(Html(html)) +} + +#[route("/queries", method = get)] +async fn queries( + Query(Num(num)): Query, + StateRef(pool): StateRef<'_, Pool>, +) -> HandleResult> { + pool.get_worlds(num).await.map(Json) +} + +#[route("/updates", method = get)] +async fn updates( + Query(Num(num)): Query, + StateRef(pool): StateRef<'_, Pool>, +) -> HandleResult> { + pool.update(num).await.map(Json) +} diff --git a/frameworks/Rust/xitca-web/src/main_sync.rs b/frameworks/Rust/xitca-web/src/main_sync.rs index 03f6c9ea3a3..ae49ebf5827 100644 --- a/frameworks/Rust/xitca-web/src/main_sync.rs +++ b/frameworks/Rust/xitca-web/src/main_sync.rs @@ -27,6 +27,7 @@ fn main() -> std::io::Result<()> { .at_typed(updates) .map(header) .serve() + .disable_vectored_write() .bind("0.0.0.0:8080")? .run() .wait() @@ -45,24 +46,16 @@ fn db(StateOwn(pool): StateOwn) -> HandleResult> { #[route("/fortunes", method = get)] fn fortunes(StateOwn(pool): StateOwn) -> HandleResult> { use sailfish::TemplateOnce; - pool.tell_fortune()? - .render_once() - .map(Html) - .map_err(Into::into) + let html = pool.tell_fortune()?.render_once()?; + Ok(Html(html)) } #[route("/queries", method = get)] -fn queries( - Query(Num(num)): Query, - StateOwn(pool): StateOwn, -) -> HandleResult> { +fn queries(Query(Num(num)): Query, StateOwn(pool): StateOwn) -> HandleResult> { pool.get_worlds(num).map(Json) } #[route("/updates", method = get)] -fn updates( - Query(Num(num)): Query, - StateOwn(pool): StateOwn, -) -> HandleResult> { +fn updates(Query(Num(num)): Query, StateOwn(pool): StateOwn) -> HandleResult> { pool.update(num).map(Json) } diff --git a/frameworks/Rust/xitca-web/src/main_unrealistic.rs b/frameworks/Rust/xitca-web/src/main_unrealistic.rs new file mode 100644 index 00000000000..e291799408c --- /dev/null +++ b/frameworks/Rust/xitca-web/src/main_unrealistic.rs @@ -0,0 +1,147 @@ +// unrealistic bench showcase popular tricks for boosting bench score artificially + +// custom global memory allocator don't affect real world performance in noticeable amount. +// in real world they should be used for reason like security, debug/profiling capability etc. +#[global_allocator] +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; + +#[path = "db_unrealistic.rs"] +mod db; +mod ser; +mod util; + +use std::{convert::Infallible, 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::{ + ser::Message, + util::{QueryParse, State}, +}; + +fn main() -> io::Result<()> { + let addr = "0.0.0.0:8080".parse().unwrap(); + + let cores = std::thread::available_parallelism().map(|num| num.get()).unwrap_or(56); + + 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), + }; + } + }) + }) + }) + .take(cores) + .collect::>(); + + // unrealistic due to no signal handling, not shutdown handling. when killing this process all resources that + // need clean async shutdown will be leaked. + for handle in handle { + handle.join().unwrap()?; + } + + Ok(()) +} + +async fn handler<'h>(req: Request<'h>, res: Response<'h>, state: &State) -> Response<'h, 3> { + // unrealistic due to no http method check + match req.path { + // unrealistic due to no dynamic path matching + "/plaintext" => { + // unrealistic due to no body streaming and no post processing. violating middleware feature of xitca-web + res.status(StatusCode::OK) + .header("content-type", "text/plain") + .header("server", "X") + // unrealistic content length header. + .header("content-length", "13") + .body_writer(|buf| Ok::<_, Infallible>(buf.extend_from_slice(b"Hello, World!"))) + .unwrap() + } + "/json" => res + .status(StatusCode::OK) + .header("content-type", "application/json") + .header("server", "X") + // unrealistic content length header. + .header("content-length", "27") + .body_writer(|buf| serde_json::to_writer(BufMutWriter(buf), &Message::new())) + .unwrap(), + // all database related categories are unrealistic. please reference db_unrealistic module for detail. + "/fortunes" => { + use sailfish::TemplateOnce; + let fortunes = state.client.tell_fortune().await.unwrap().render_once().unwrap(); + res.status(StatusCode::OK) + .header("content-type", "text/html; charset=utf-8") + .header("server", "X") + .body(fortunes.as_bytes()) + } + "/db" => { + // 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) + } + 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) + } + 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) + } + _ => res.status(StatusCode::NOT_FOUND).header("server", "X").body(&[]), + } +} + +fn json_response<'r, DB, T>(res: Response<'r>, state: &State, val: &T) -> Response<'r, 3> +where + T: serde::Serialize, +{ + let buf = &mut *state.write_buf.borrow_mut(); + serde_json::to_writer(BufMutWriter(buf), val).unwrap(); + let res = res + .status(StatusCode::OK) + .header("content-type", "application/json") + .header("server", "X") + .body(buf.as_ref()); + buf.clear(); + res +} diff --git a/frameworks/Rust/xitca-web/src/ser.rs b/frameworks/Rust/xitca-web/src/ser.rs index ca1c56caf70..edf9183a8c0 100644 --- a/frameworks/Rust/xitca-web/src/ser.rs +++ b/frameworks/Rust/xitca-web/src/ser.rs @@ -5,11 +5,19 @@ use std::borrow::Cow; use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; use xitca_http::{ body::Once, - bytes::{BufMutWriter, Bytes, BytesMut}, - http::{const_header_value::JSON, header::CONTENT_TYPE, IntoResponse, Request, Response}, + bytes::{BufMutWriter, Bytes}, + http::{ + self, + const_header_value::{JSON, TEXT_HTML_UTF8, TEXT_UTF8}, + header::CONTENT_TYPE, + IntoResponse as _, RequestExt, StatusCode, + }, }; -use crate::util::Error; +use crate::util::{Error, State}; + +const HELLO: &str = "Hello, World!"; +const HELLO_BYTES: &[u8] = HELLO.as_bytes(); #[derive(Clone)] pub struct Message { @@ -19,15 +27,13 @@ pub struct Message { impl Message { #[inline] pub const fn new() -> Self { - Self { - message: "Hello, World!", - } + Self { message: HELLO } } } pub struct Num(pub u16); -#[cfg_attr(feature = "pg-orm", derive(diesel::Queryable))] +#[cfg_attr(any(feature = "pg-orm", feature = "pg-orm-async"), derive(diesel::Queryable))] pub struct World { pub id: i32, pub randomnumber: i32, @@ -40,7 +46,7 @@ impl World { } } -#[cfg_attr(feature = "pg-orm", derive(diesel::Queryable))] +#[cfg_attr(any(feature = "pg-orm", feature = "pg-orm-async"), derive(diesel::Queryable))] pub struct Fortune { pub id: i32, pub message: Cow<'static, str>, @@ -56,16 +62,41 @@ impl Fortune { } } -// TODO: use another template engine with faster compile time.(perferably with no proc macro) -#[cfg_attr( - feature = "template", - derive(sailfish::TemplateOnce), - template(path = "fortune.stpl", rm_whitespace = true) -)] pub struct Fortunes { items: Vec, } +// this is roughly the code generated by sailfish::TemplateOnce macro. +// using the macro does not have any perf cost and this piece of code is expanded manually to speed up compile time of +// bench to reduce resource usage of bench runner +#[cfg(feature = "template")] +impl sailfish::TemplateOnce for Fortunes { + fn render_once(self) -> sailfish::RenderResult { + use sailfish::runtime::{Buffer, Render}; + + const PREFIX: &str = "\n\nFortunes\n\n\n\n"; + const SUFFIX: &str = "\n
idmessage
\n\n"; + + let mut buf = Buffer::with_capacity(1236); + + buf.push_str(PREFIX); + for item in self.items { + buf.push_str(""); + Render::render_escaped(&item.id, &mut buf)?; + buf.push_str(""); + Render::render_escaped(&item.message, &mut buf)?; + buf.push_str(""); + } + buf.push_str(SUFFIX); + + Ok(buf.into_string()) + } + + fn render_once_to(self, _: &mut sailfish::runtime::Buffer) -> Result<(), sailfish::runtime::RenderError> { + unimplemented!("") + } +} + impl Fortunes { #[inline] pub const fn new(items: Vec) -> Self { @@ -78,11 +109,11 @@ impl<'de> Deserialize<'de> for Num { where D: Deserializer<'de>, { - use core::{cmp, fmt}; + use core::fmt; use serde::de::{Error, MapAccess, Visitor}; - const FIELDS: &'static [&'static str] = &["q"]; + const FIELDS: &[&str] = &["q"]; struct Field; @@ -128,11 +159,8 @@ impl<'de> Deserialize<'de> for Num { where V: MapAccess<'de>, { - map.next_key::()? - .ok_or_else(|| Error::missing_field("q"))?; - let q = map.next_value::().unwrap_or(1); - let q = cmp::min(500, cmp::max(1, q)); - Ok(Num(q)) + map.next_key::()?.ok_or_else(|| Error::missing_field("q"))?; + Ok(Num(map.next_value().unwrap_or(1).clamp(1, 500))) } } @@ -163,16 +191,42 @@ impl Serialize for World { } } -pub fn json_response( - req: Request, - buf: &mut BytesMut, - value: &S, -) -> Result>, Error> -where - S: ?Sized + Serialize, -{ - serde_json::to_writer(BufMutWriter(buf), value)?; - let mut res = req.into_response(buf.split().freeze()); - res.headers_mut().insert(CONTENT_TYPE, JSON); - Ok(res) +pub type Request = http::Request>; +pub type Response = http::Response>; + +pub trait IntoResponse: Sized { + fn json_response(self, state: &State, val: &impl Serialize) -> Result; + + fn text_response(self) -> Result; + + fn html_response(self, val: String) -> Result; +} + +impl IntoResponse for Request { + fn json_response(self, state: &State, val: &impl Serialize) -> Result { + let buf = &mut *state.write_buf.borrow_mut(); + serde_json::to_writer(BufMutWriter(buf), val)?; + let mut res = self.into_response(buf.split().freeze()); + res.headers_mut().insert(CONTENT_TYPE, JSON); + Ok(res) + } + + fn text_response(self) -> Result { + let mut res = self.into_response(const { Bytes::from_static(HELLO_BYTES) }); + res.headers_mut().insert(CONTENT_TYPE, TEXT_UTF8); + Ok(res) + } + + fn html_response(self, val: String) -> Result { + let mut res = self.into_response(Bytes::from(val)); + res.headers_mut().insert(CONTENT_TYPE, TEXT_HTML_UTF8); + Ok(res) + } +} + +pub fn error_response(status: StatusCode) -> Response { + http::Response::builder() + .status(status) + .body(Once::new(Bytes::new())) + .unwrap() } diff --git a/frameworks/Rust/xitca-web/src/util.rs b/frameworks/Rust/xitca-web/src/util.rs index 8c4cee9da98..3998a2ef1ec 100755 --- a/frameworks/Rust/xitca-web/src/util.rs +++ b/frameworks/Rust/xitca-web/src/util.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use core::{cell::RefCell, cmp}; +use core::cell::RefCell; use xitca_http::{bytes::BytesMut, http::header::HeaderValue}; @@ -10,15 +10,15 @@ pub trait QueryParse { impl QueryParse for Option<&str> { fn parse_query(self) -> u16 { - let num = self - .and_then(|this| { - use atoi::FromRadix10; - this.find('q') - .map(|pos| u16::from_radix_10(this.split_at(pos + 2).1.as_ref()).0) - }) - .unwrap_or(1); - - cmp::min(500, cmp::max(1, num)) + self.and_then(|q| q.find('q').map(|pos| q.split_at(pos + 2).1.parse_query())) + .unwrap_or(1) + } +} + +impl QueryParse for &str { + fn parse_query(self) -> u16 { + use atoi::FromRadix10; + u16::from_radix_10(self.as_bytes()).0.clamp(1, 500) } } @@ -36,51 +36,33 @@ pub struct State { pub write_buf: RefCell, } +impl State { + pub fn new(client: DB) -> Self { + Self { + client, + write_buf: Default::default(), + } + } +} + #[cfg(not(target_arch = "wasm32"))] -mod non_wasm { - #[derive(Default)] - pub struct Rand(nanorand::WyRand); +pub mod non_wasm { + use rand::{rngs::SmallRng, Rng, SeedableRng}; - impl Rand { - #[inline] - pub fn gen_id(&mut self) -> i32 { - use nanorand::Rng; - (self.0.generate::() % 10_000 + 1) as _ + pub struct Rand(SmallRng); + + impl Default for Rand { + fn default() -> Self { + Self(SmallRng::from_entropy()) } } - #[cfg(any(feature = "pg", feature = "pg-iou"))] - mod pg_state { - use core::{cell::RefCell, future::Future, pin::Pin}; - - use xitca_http::{ - bytes::BytesMut, - util::middleware::context::{Context, ContextBuilder}, - }; - - use crate::{ - db::{self, Client}, - util::{HandleResult, State}, - }; - - pub type Ctx<'a, Req> = Context<'a, Req, State>; - - pub fn context_mw( - ) -> ContextBuilder Pin>>>>> - { - ContextBuilder::new(|| { - Box::pin(async { - db::create().await.map(|client| State { - client, - write_buf: RefCell::new(BytesMut::new()), - }) - }) as _ - }) + impl Rand { + #[inline] + pub fn gen_id(&mut self) -> i32 { + self.0.gen_range(1..=10000) } } - - #[cfg(any(feature = "pg", feature = "pg-iou"))] - pub use pg_state::*; } #[cfg(not(target_arch = "wasm32"))] diff --git a/frameworks/Rust/xitca-web/templates/fortune.stpl b/frameworks/Rust/xitca-web/templates/fortune.stpl index eb1abe6a4fa..28227945b4c 100644 --- a/frameworks/Rust/xitca-web/templates/fortune.stpl +++ b/frameworks/Rust/xitca-web/templates/fortune.stpl @@ -4,7 +4,7 @@ - <% for item in items { %><% } %> + <% for item in self.items { %><% } %>
idmessage
<%= item.id %><%= &*item.message %>
<%= item.id %><%= &*item.message %>
\ No newline at end of file diff --git a/frameworks/Rust/xitca-web/xitca-web-axum.dockerfile b/frameworks/Rust/xitca-web/xitca-web-axum.dockerfile deleted file mode 100644 index 2d29f5e2ffb..00000000000 --- a/frameworks/Rust/xitca-web/xitca-web-axum.dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM rust:1.77 - -ADD ./ /xitca-web -WORKDIR /xitca-web - -RUN cargo build --release --bin xitca-web-axum --features axum,io-uring,perf,pg-sync,template - -EXPOSE 8080 - -CMD ./target/release/xitca-web-axum diff --git a/frameworks/Rust/xitca-web/xitca-web-iou.dockerfile b/frameworks/Rust/xitca-web/xitca-web-iou.dockerfile deleted file mode 100644 index 43bf2b23fc5..00000000000 --- a/frameworks/Rust/xitca-web/xitca-web-iou.dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM rust:1.77 - -ADD ./ /xitca-web -WORKDIR /xitca-web - -RUN cargo build --release --bin xitca-web-iou --features io-uring,perf,pg-iou,template - -EXPOSE 8080 - -CMD ./target/release/xitca-web-iou diff --git a/frameworks/Rust/xitca-web/xitca-web-orm.dockerfile b/frameworks/Rust/xitca-web/xitca-web-orm.dockerfile new file mode 100644 index 00000000000..06e40825b00 --- /dev/null +++ b/frameworks/Rust/xitca-web/xitca-web-orm.dockerfile @@ -0,0 +1,10 @@ +FROM rust:1.81 + +ADD ./ /xitca-web +WORKDIR /xitca-web + +RUN cargo build --release --bin xitca-web-orm --features pg-orm-async,template,web-codegen + +EXPOSE 8080 + +CMD ./target/release/xitca-web-orm diff --git a/frameworks/Rust/xitca-web/xitca-web-sync.dockerfile b/frameworks/Rust/xitca-web/xitca-web-sync.dockerfile index 5e71fa5d649..ad2602a8606 100644 --- a/frameworks/Rust/xitca-web/xitca-web-sync.dockerfile +++ b/frameworks/Rust/xitca-web/xitca-web-sync.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.77 +FROM rust:1.81 ADD ./ /xitca-web WORKDIR /xitca-web diff --git a/frameworks/Rust/xitca-web/xitca-web-unrealistic.dockerfile b/frameworks/Rust/xitca-web/xitca-web-unrealistic.dockerfile new file mode 100644 index 00000000000..f202947acc0 --- /dev/null +++ b/frameworks/Rust/xitca-web/xitca-web-unrealistic.dockerfile @@ -0,0 +1,10 @@ +FROM rust:1.81 + +ADD ./ /xitca-web +WORKDIR /xitca-web + +RUN cargo build --release --bin xitca-web-unrealistic --features perf,pg,template + +EXPOSE 8080 + +CMD ./target/release/xitca-web-unrealistic diff --git a/frameworks/Rust/xitca-web/xitca-web-wasm.dockerfile b/frameworks/Rust/xitca-web/xitca-web-wasm.dockerfile index 51aa96fd4eb..1c22986b0a2 100644 --- a/frameworks/Rust/xitca-web/xitca-web-wasm.dockerfile +++ b/frameworks/Rust/xitca-web/xitca-web-wasm.dockerfile @@ -1,7 +1,7 @@ ARG WASMTIME_VERSION=15.0.0 ARG WASM_TARGET=wasm32-wasip1-threads -FROM rust:1.77 AS compile +FROM rust:1.81 AS compile ARG WASMTIME_VERSION ARG WASM_TARGET diff --git a/frameworks/Rust/xitca-web/xitca-web.dockerfile b/frameworks/Rust/xitca-web/xitca-web.dockerfile index bed37320e72..d928183362f 100644 --- a/frameworks/Rust/xitca-web/xitca-web.dockerfile +++ b/frameworks/Rust/xitca-web/xitca-web.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.77 +FROM rust:1.81 ADD ./ /xitca-web WORKDIR /xitca-web diff --git a/frameworks/Scala/otavia/.mill-version b/frameworks/Scala/otavia/.mill-version new file mode 100644 index 00000000000..772c67a500f --- /dev/null +++ b/frameworks/Scala/otavia/.mill-version @@ -0,0 +1 @@ +0.11.8 \ No newline at end of file diff --git a/frameworks/Scala/otavia/.scalafmt.conf b/frameworks/Scala/otavia/.scalafmt.conf new file mode 100644 index 00000000000..ff856188419 --- /dev/null +++ b/frameworks/Scala/otavia/.scalafmt.conf @@ -0,0 +1,16 @@ +version = "3.5.3" + +runner.dialect = scala3 +maxColumn = 120 +docstrings.blankFirstLine = no +docstrings.style = AsteriskSpace +docstrings.removeEmpty = true +docstrings.oneline = fold +docstrings.wrap = yes +docstrings.wrapMaxColumn = 120 +docstrings.forceBlankLineBefore = true +align.preset = more + +indent.main = 4 + +newlines.topLevelBodyIfMinStatements = [before,after] \ No newline at end of file diff --git a/frameworks/Scala/otavia/README.md b/frameworks/Scala/otavia/README.md new file mode 100644 index 00000000000..0ea11222811 --- /dev/null +++ b/frameworks/Scala/otavia/README.md @@ -0,0 +1,13 @@ +## Introduction + +[GitHub - otavia-projects/otavia : Your shiny new IO & Actor programming model!](https://github.com/otavia-projects/otavia) + +`otavia` is an IO and Actor programming model power by Scala 3, it provides a toolkit to make writing high-performance +concurrent programs more easily. + +You can get a quick overview of the basic usage and core design of `otavia` in the following documentation: + +- [Quick Start](https://github.com/otavia-projects/otavia/blob/main/docs/_docs/quick_start.md) +- [Core Concepts and Design](https://github.com/otavia-projects/otavia/blob/main/docs/_docs/core_concept.md) + +More document can be found at [website](https://otavia.cc/home.html) \ No newline at end of file diff --git a/frameworks/Scala/otavia/benchmark/src/app/controller/DBController.scala b/frameworks/Scala/otavia/benchmark/src/app/controller/DBController.scala new file mode 100644 index 00000000000..2e9e32abbe7 --- /dev/null +++ b/frameworks/Scala/otavia/benchmark/src/app/controller/DBController.scala @@ -0,0 +1,103 @@ +package app.controller + +import app.controller.DBController.* +import app.model.World +import cc.otavia.core.actor.{MessageOf, StateActor} +import cc.otavia.core.address.Address +import cc.otavia.core.message.{Ask, Reply} +import cc.otavia.core.stack.helper.{FutureState, FuturesState, StartState} +import cc.otavia.core.stack.{AskStack, StackState, StackYield} +import cc.otavia.http.server.{HttpRequest, HttpResponse} +import cc.otavia.sql.Connection +import cc.otavia.sql.statement.{ModifyRows, PrepareQuery} + +import java.util.SplittableRandom + +class DBController extends StateActor[REQ] { + + private var connection: Address[MessageOf[Connection]] = _ + + private val random = new SplittableRandom() + + override protected def afterMount(): Unit = connection = autowire[Connection]() + + override protected def resumeAsk(stack: AskStack[REQ & Ask[? <: Reply]]): StackYield = + stack.ask match + case _: SingleQueryRequest => handleSingleQuery(stack.asInstanceOf[AskStack[SingleQueryRequest]]) + case _: MultipleQueryRequest => handleMultipleQuery(stack.asInstanceOf[AskStack[MultipleQueryRequest]]) + case _: UpdateRequest => handleUpdateQuery(stack.asInstanceOf[AskStack[UpdateRequest]]) + + // Test 2: Single database query + private def handleSingleQuery(stack: AskStack[SingleQueryRequest]): StackYield = { + stack.state match + case _: StartState => + val state = FutureState[World]() + connection.ask(PrepareQuery.fetchOne[World](SELECT_WORLD, randomWorld()), state.future) + stack.suspend(state) + case state: FutureState[World] => + stack.`return`(state.future.getNow) + } + + // Test 3: Multiple database queries + private def handleMultipleQuery(stack: AskStack[MultipleQueryRequest]): StackYield = { + stack.state match + case _: StartState => + stack.suspend(selectWorlds(normalizeQueries(stack.ask.params))) + case state: FuturesState[World] => + val response = HttpResponse.builder.setContent(state.futures.map(_.getNow)).build() + stack.`return`(response) + } + + // Test 5: Database updates + private def handleUpdateQuery(stack: AskStack[UpdateRequest]): StackYield = { + stack.state match + case _: StartState => + stack.suspend(selectWorlds(normalizeQueries(stack.ask.params))) + case state: FuturesState[World] => + val worlds = state.futures.map(_.getNow) + stack.attach(worlds) + val newState = FutureState[ModifyRows]() + val newWorlds = worlds.sortBy(_.id).map(_.copy(randomNumber = randomWorld())) + connection.ask(PrepareQuery.updateBatch(UPDATE_WORLD, newWorlds), newState.future) + stack.suspend(newState) + case state: FutureState[ModifyRows] => + if (state.future.isFailed) state.future.causeUnsafe.printStackTrace() + val response = HttpResponse.builder.setContent(stack.attach[Seq[World]]).build() + stack.`return`(response) + } + + private def selectWorlds(queries: Int): StackState = { + val state = FuturesState[World](queries) + for (future <- state.futures) + connection.ask(PrepareQuery.fetchOne[World](SELECT_WORLD, randomWorld()), future) + state + } + + private def randomWorld(): Int = 1 + random.nextInt(10000) + + private def normalizeQueries(params: Map[String, String]): Int = { + params.get("queries") match + case Some(value) => + try { + val queries = value.toInt + if (queries < 1) 1 else if (queries > 500) 500 else queries + } catch { + case e: Throwable => 1 + } + case None => 1 + } + +} + +object DBController { + + type REQ = SingleQueryRequest | MultipleQueryRequest | UpdateRequest + + class SingleQueryRequest extends HttpRequest[Nothing, World] + class MultipleQueryRequest extends HttpRequest[Nothing, HttpResponse[Seq[World]]] + class UpdateRequest extends HttpRequest[Nothing, HttpResponse[Seq[World]]] + + private val SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1" + private val UPDATE_WORLD = "update world set randomnumber=$2 where id=$1" + +} diff --git a/frameworks/Scala/otavia/benchmark/src/app/controller/FortuneController.scala b/frameworks/Scala/otavia/benchmark/src/app/controller/FortuneController.scala new file mode 100644 index 00000000000..4f7fe7bf800 --- /dev/null +++ b/frameworks/Scala/otavia/benchmark/src/app/controller/FortuneController.scala @@ -0,0 +1,51 @@ +package app.controller + +import app.controller.FortuneController.* +import app.model.Fortune +import cc.otavia.core.actor.{MessageOf, StateActor} +import cc.otavia.core.address.Address +import cc.otavia.core.stack.helper.{FutureState, StartState} +import cc.otavia.core.stack.{AskStack, StackState, StackYield} +import cc.otavia.http.server.{HttpRequest, HttpResponse} +import cc.otavia.sql.statement.PrepareQuery +import cc.otavia.sql.{Connection, RowSet} + +import java.util +import java.util.Comparator + +class FortuneController extends StateActor[FortuneRequest] { + + private var connection: Address[MessageOf[Connection]] = _ + private val tmpArray: Array[Fortune] = new Array[Fortune](13) + + private val comparator = new Comparator[Fortune] { + override def compare(o1: Fortune, o2: Fortune): Int = o1.message.compareTo(o2.message) + } + + override protected def afterMount(): Unit = connection = autowire[Connection]() + + // Test 4: Fortunes + override protected def resumeAsk(stack: AskStack[FortuneRequest]): StackYield = { + stack.state match + case _: StartState => + val state = FutureState[RowSet[Fortune]]() + connection.ask(PrepareQuery.fetchAll[Fortune](SELECT_FORTUNE), state.future) + stack.suspend(state) + case state: FutureState[RowSet[Fortune]] => + System.arraycopy(state.future.getNow.rows, 0, tmpArray, 0, 12) + tmpArray(12) = Fortune(0, "Additional fortune added at request time.") + util.Arrays.sort(tmpArray, comparator) + val fortunes = tmpArray.clone() + val response = HttpResponse.builder.setContent(fortunes).build() + stack.`return`(response) + } + +} + +object FortuneController { + + class FortuneRequest extends HttpRequest[Nothing, HttpResponse[Array[Fortune]]] + + private val SELECT_FORTUNE = "SELECT id, message from FORTUNE" + +} diff --git a/frameworks/Scala/otavia/benchmark/src/app/model/Fortune.scala b/frameworks/Scala/otavia/benchmark/src/app/model/Fortune.scala new file mode 100644 index 00000000000..1df592954a4 --- /dev/null +++ b/frameworks/Scala/otavia/benchmark/src/app/model/Fortune.scala @@ -0,0 +1,7 @@ +package app.model + +import cc.otavia.json.JsonSerde +import cc.otavia.sql.{Row, RowCodec} + +/** The model for the "fortune" database table. */ +case class Fortune(id: Int, message: String) extends Row derives RowCodec, JsonSerde diff --git a/frameworks/Scala/otavia/benchmark/src/app/model/Message.scala b/frameworks/Scala/otavia/benchmark/src/app/model/Message.scala new file mode 100644 index 00000000000..a0d389090ae --- /dev/null +++ b/frameworks/Scala/otavia/benchmark/src/app/model/Message.scala @@ -0,0 +1,5 @@ +package app.model + +import cc.otavia.json.JsonSerde + +case class Message(message: String) derives JsonSerde diff --git a/frameworks/Scala/otavia/benchmark/src/app/model/World.scala b/frameworks/Scala/otavia/benchmark/src/app/model/World.scala new file mode 100644 index 00000000000..af6f8539d74 --- /dev/null +++ b/frameworks/Scala/otavia/benchmark/src/app/model/World.scala @@ -0,0 +1,8 @@ +package app.model + +import cc.otavia.json.JsonSerde +import cc.otavia.serde.annotation.rename +import cc.otavia.sql.{Row, RowCodec} + +/** The model for the "world" database table. */ +case class World(id: Int, randomNumber: Int) extends Row derives RowCodec, JsonSerde diff --git a/frameworks/Scala/otavia/benchmark/src/app/startup.scala b/frameworks/Scala/otavia/benchmark/src/app/startup.scala new file mode 100644 index 00000000000..32eb0c2e539 --- /dev/null +++ b/frameworks/Scala/otavia/benchmark/src/app/startup.scala @@ -0,0 +1,70 @@ +package app + +import app.controller.DBController.* +import app.controller.FortuneController.* +import app.controller.{DBController, FortuneController} +import app.model.* +import app.util.FortunesRender +import cc.otavia.core.actor.ChannelsActor.{Bind, ChannelEstablished} +import cc.otavia.core.actor.MainActor +import cc.otavia.core.slf4a.LoggerFactory +import cc.otavia.core.stack.helper.{FutureState, StartState} +import cc.otavia.core.stack.{NoticeStack, StackYield} +import cc.otavia.core.system.ActorSystem +import cc.otavia.http.HttpMethod.* +import cc.otavia.http.MediaType +import cc.otavia.http.MediaType.* +import cc.otavia.http.server.* +import cc.otavia.http.server.Router.* +import cc.otavia.json.JsonSerde +import cc.otavia.serde.helper.BytesSerde +import cc.otavia.sql.Connection + +import java.io.File +import java.nio.charset.StandardCharsets.UTF_8 +import java.nio.file.Path + +private class ServerMain(val port: Int = 8080) extends MainActor(Array.empty) { + + override def main0(stack: NoticeStack[MainActor.Args]): StackYield = stack.state match + case _: StartState => + val worldResponseSerde = HttpResponseSerde.json(summon[JsonSerde[World]]) + val worldsResponseSerde = HttpResponseSerde.json(JsonSerde.derived[Seq[World]]) + val fortunesResponseSerde = HttpResponseSerde(new FortunesRender(), MediaType.TEXT_HTML_UTF8) + + val dbController = autowire[DBController]() + val fortuneController = autowire[FortuneController]() + + val routers = Seq( + // Test 6: plaintext + constant[Array[Byte]](GET, "/plaintext", "Hello, World!".getBytes(UTF_8), BytesSerde, TEXT_PLAIN_UTF8), + // Test 1: JSON serialization + constant[Message](GET, "/json", Message("Hello, World!"), summon[JsonSerde[Message]], APP_JSON), + // Test 2: Single database query. + get("/db", dbController, () => new SingleQueryRequest(), worldResponseSerde), + // Test 3: Multiple database queries + get("/queries", dbController, () => new MultipleQueryRequest(), worldsResponseSerde), + // Test 5: Database updates + get("/updates", dbController, () => new UpdateRequest(), worldsResponseSerde), + // Test 4: Fortunes + get("/fortunes", fortuneController, () => new FortuneRequest(), fortunesResponseSerde) + ) + val server = system.buildActor(() => new HttpServer(system.actorWorkerSize, routers)) + val state = FutureState[ChannelEstablished]() + server.ask(Bind(port), state.future) + stack.suspend(state) + case state: FutureState[ChannelEstablished] => + if (state.future.isFailed) state.future.causeUnsafe.printStackTrace() + logger.info(s"http server bind port $port success") + stack.`return`() + +} + +@main def startup(url: String, user: String, password: String, poolSize: Int): Unit = + val system = ActorSystem() + val logger = LoggerFactory.getLogger("startup", system) + logger.info("starting http server") + system.buildActor(() => new Connection(url, user, password), global = true, num = poolSize) + system.buildActor(() => new DBController(), global = true, num = system.actorWorkerSize) + system.buildActor(() => new FortuneController(), global = true, num = system.actorWorkerSize) + system.buildActor(() => new ServerMain()) diff --git a/frameworks/Scala/otavia/benchmark/src/app/util/FortunesRender.scala b/frameworks/Scala/otavia/benchmark/src/app/util/FortunesRender.scala new file mode 100644 index 00000000000..69401305550 --- /dev/null +++ b/frameworks/Scala/otavia/benchmark/src/app/util/FortunesRender.scala @@ -0,0 +1,64 @@ +package app.util + +import app.model.Fortune +import cc.otavia.buffer.{Buffer, BufferUtils} +import cc.otavia.serde.Serde + +import java.nio.charset.StandardCharsets +import scala.annotation.switch + +class FortunesRender extends Serde[Array[Fortune]] { + + private val text1 = + "Fortunes" + .getBytes(StandardCharsets.UTF_8) + + private val text2 = "".getBytes(StandardCharsets.UTF_8) + + private val text5 = "
idmessage
".getBytes(StandardCharsets.UTF_8) + + private val text3 = "".getBytes(StandardCharsets.UTF_8) + + private val text4 = "
".getBytes(StandardCharsets.UTF_8) + + private val lt = "<".getBytes() + private val gt = ">".getBytes() + private val quot = """.getBytes() + private val squot = "'".getBytes() + private val amp = "&".getBytes() + + override def serialize(fortunes: Array[Fortune], out: Buffer): Unit = { + out.writeBytes(text1) + for (fortune <- fortunes) { + out.writeBytes(text2) + BufferUtils.writeIntAsString(out, fortune.id) + out.writeBytes(text3) + writeEscapeMessage(out, fortune.message) + out.writeBytes(text4) + } + out.writeBytes(text5) + } + + override def deserialize(in: Buffer): Array[Fortune] = throw new UnsupportedOperationException() + + private def writeEscapeMessage(buffer: Buffer, message: String): Unit = { + var i = 0 + while (i < message.length) { + val ch = message.charAt(i) + writeChar(buffer, ch) + i += 1 + } + } + + private def writeChar(buffer: Buffer, ch: Char): Unit = (ch: @switch) match + case '<' => buffer.writeBytes(lt) + case '>' => buffer.writeBytes(gt) + case '"' => buffer.writeBytes(quot) + case '\'' => buffer.writeBytes(squot) + case '&' => buffer.writeBytes(amp) + case _ => + if (ch < 0x80) buffer.writeByte(ch.toByte) + else if (ch < 0x800) buffer.writeShortLE((ch >> 6 | (ch << 8 & 0x3f00) | 0x80c0).toShort) + else buffer.writeMediumLE(ch >> 12 | (ch << 2 & 0x3f00) | (ch << 16 & 0x3f0000) | 0x8080e0) + +} diff --git a/frameworks/Scala/otavia/benchmark_config.json b/frameworks/Scala/otavia/benchmark_config.json new file mode 100644 index 00000000000..87204fa771d --- /dev/null +++ b/frameworks/Scala/otavia/benchmark_config.json @@ -0,0 +1,53 @@ +{ + "framework": "otavia", + "tests": [ + { + "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": "Postgres", + "framework": "otavia", + "language": "Scala", + "flavor": "None", + "orm": "Micro", + "platform": "Otavia", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "otavia", + "notes": "", + "versus": "Otavia" + }, + "overshoot": { + "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": "Postgres", + "framework": "otavia", + "language": "Scala", + "flavor": "None", + "orm": "Micro", + "platform": "Otavia", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "otavia", + "notes": "", + "versus": "Otavia" + } + } + ] +} diff --git a/frameworks/Scala/otavia/build.sc b/frameworks/Scala/otavia/build.sc new file mode 100644 index 00000000000..c2bf1888dc9 --- /dev/null +++ b/frameworks/Scala/otavia/build.sc @@ -0,0 +1,15 @@ +import mill._ +import mill.scalalib._ + +def otaviaVersion = "0.4.5" + +object benchmark extends ScalaModule { + + override def scalaVersion = "3.3.3" + + override def ivyDeps = Agg( + ivy"cc.otavia::otavia-codec-http:$otaviaVersion", + ivy"cc.otavia::otavia-postgres-driver:$otaviaVersion" + ) + +} diff --git a/frameworks/Scala/otavia/config.toml b/frameworks/Scala/otavia/config.toml new file mode 100644 index 00000000000..3a8b7f85e94 --- /dev/null +++ b/frameworks/Scala/otavia/config.toml @@ -0,0 +1,36 @@ +[framework] +name = "otavia" + +[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 = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Micro" +platform = "Otavia" +webserver = "None" +versus = "Otavia" + +[overshoot] +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 = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Micro" +platform = "Otavia" +webserver = "None" +versus = "Otavia" \ No newline at end of file diff --git a/frameworks/Scala/otavia/millw b/frameworks/Scala/otavia/millw new file mode 100644 index 00000000000..73bb4d0e4a0 --- /dev/null +++ b/frameworks/Scala/otavia/millw @@ -0,0 +1,194 @@ +#!/usr/bin/env sh + +# This is a wrapper script, that automatically download mill from GitHub release pages +# You can give the required mill version with --mill-version parameter +# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION +# +# Project page: https://github.com/lefou/millw +# Script Version: 0.4.6 +# +# If you want to improve this script, please also contribute your changes back! +# +# Licensed under the Apache License, Version 2.0 + +set -e + +if [ -z "${DEFAULT_MILL_VERSION}" ] ; then + DEFAULT_MILL_VERSION="0.10.10" +fi + + +if [ -z "${GITHUB_RELEASE_CDN}" ] ; then + GITHUB_RELEASE_CDN="" +fi + + +MILL_REPO_URL="https://github.com/com-lihaoyi/mill" + +if [ -z "${CURL_CMD}" ] ; then + CURL_CMD=curl +fi + +# Explicit commandline argument takes precedence over all other methods +if [ "$1" = "--mill-version" ] ; then + shift + if [ "x$1" != "x" ] ; then + MILL_VERSION="$1" + shift + else + echo "You specified --mill-version without a version." 1>&2 + echo "Please provide a version that matches one provided on" 1>&2 + echo "${MILL_REPO_URL}/releases" 1>&2 + false + fi +fi + +# Please note, that if a MILL_VERSION is already set in the environment, +# We reuse it's value and skip searching for a value. + +# If not already set, read .mill-version file +if [ -z "${MILL_VERSION}" ] ; then + if [ -f ".mill-version" ] ; then + MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)" + elif [ -f ".config/mill-version" ] ; then + MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)" + fi +fi + +if [ -n "${XDG_CACHE_HOME}" ] ; then + MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download" +else + MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download" +fi + +# If not already set, try to fetch newest from Github +if [ -z "${MILL_VERSION}" ] ; then + # TODO: try to load latest version from release page + echo "No mill version specified." 1>&2 + echo "You should provide a version via '.mill-version' file or --mill-version option." 1>&2 + + mkdir -p "${MILL_DOWNLOAD_PATH}" + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || ( + # we might be on OSX or BSD which don't have -d option for touch + # but probably a -A [-][[hh]mm]SS + touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) || ( + # in case we still failed, we retry the first touch command with the intention + # to show the (previously suppressed) error message + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) + + # POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993 + # if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then + if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then + # we know a current latest version + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # we don't know a current latest version + echo "Retrieving latest mill version ..." 1>&2 + LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest" + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # Last resort + MILL_VERSION="${DEFAULT_MILL_VERSION}" + echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2 + else + echo "Using mill version ${MILL_VERSION}" 1>&2 + fi +fi + +MILL="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}" + +try_to_use_system_mill() { + MILL_IN_PATH="$(command -v mill || true)" + + if [ -z "${MILL_IN_PATH}" ]; then + return + fi + + UNIVERSAL_SCRIPT_MAGIC="@ 2>/dev/null # 2>nul & echo off & goto BOF" + + if ! head -c 128 "${MILL_IN_PATH}" | grep -qF "${UNIVERSAL_SCRIPT_MAGIC}"; then + if [ -n "${MILLW_VERBOSE}" ]; then + echo "Could not determine mill version of ${MILL_IN_PATH}, as it does not start with the universal script magic2" 1>&2 + fi + return + fi + + # Roughly the size of the universal script. + MILL_VERSION_SEARCH_RANGE="2403" + MILL_IN_PATH_VERSION=$(head -c "${MILL_VERSION_SEARCH_RANGE}" "${MILL_IN_PATH}" |\ + sed -n 's/^.*-DMILL_VERSION=\([^\s]*\) .*$/\1/p' |\ + head -n 1) + + if [ -z "${MILL_IN_PATH_VERSION}" ]; then + echo "Could not determine mill version, even though ${MILL_IN_PATH} has the universal script magic" 1>&2 + return + fi + + if [ "${MILL_IN_PATH_VERSION}" = "${MILL_VERSION}" ]; then + MILL="${MILL_IN_PATH}" + fi +} +try_to_use_system_mill + +# If not already downloaded, download it +if [ ! -s "${MILL}" ] ; then + + # support old non-XDG download dir + MILL_OLD_DOWNLOAD_PATH="${HOME}/.mill/download" + OLD_MILL="${MILL_OLD_DOWNLOAD_PATH}/${MILL_VERSION}" + if [ -x "${OLD_MILL}" ] ; then + MILL="${OLD_MILL}" + else + VERSION_PREFIX="$(echo $MILL_VERSION | cut -b -4)" + case $VERSION_PREFIX in + 0.0. | 0.1. | 0.2. | 0.3. | 0.4. ) + DOWNLOAD_SUFFIX="" + ;; + *) + DOWNLOAD_SUFFIX="-assembly" + ;; + esac + unset VERSION_PREFIX + + DOWNLOAD_FILE=$(mktemp mill.XXXXXX) + MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/') + DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}" + # TODO: handle command not found + echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2 + ${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" ${DOWNLOAD_URL} + chmod +x "${DOWNLOAD_FILE}" + mkdir -p "${MILL_DOWNLOAD_PATH}" + mv "${DOWNLOAD_FILE}" "${MILL}" + + unset DOWNLOAD_FILE + unset DOWNLOAD_SUFFIX + fi +fi + +if [ -z "$MILL_MAIN_CLI" ] ; then + MILL_MAIN_CLI="${0}" +fi + +MILL_FIRST_ARG="" +if [ "$1" = "--bsp" ] || [ "$1" = "-i" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then + # Need to preserve the first position of those listed options + MILL_FIRST_ARG=$1 + shift +fi + +unset MILL_DOWNLOAD_PATH +unset MILL_OLD_DOWNLOAD_PATH +unset OLD_MILL +unset MILL_VERSION +unset MILL_VERSION_TAG +unset MILL_REPO_URL + +# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes +# shellcheck disable=SC2086 +exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@" diff --git a/frameworks/Scala/otavia/millw.bat b/frameworks/Scala/otavia/millw.bat new file mode 100644 index 00000000000..6359e35e500 --- /dev/null +++ b/frameworks/Scala/otavia/millw.bat @@ -0,0 +1,173 @@ +@echo off + +rem This is a wrapper script, that automatically download mill from GitHub release pages +rem You can give the required mill version with --mill-version parameter +rem If no version is given, it falls back to the value of DEFAULT_MILL_VERSION +rem +rem Project page: https://github.com/lefou/millw +rem Script Version: 0.4.6 +rem +rem If you want to improve this script, please also contribute your changes back! +rem +rem Licensed under the Apache License, Version 2.0 + +rem setlocal seems to be unavailable on Windows 95/98/ME +rem but I don't think we need to support them in 2019 +setlocal enabledelayedexpansion + +if [!DEFAULT_MILL_VERSION!]==[] ( + set "DEFAULT_MILL_VERSION=0.10.10" +) + +if [!GITHUB_RELEASE_CDN!]==[] ( + set "GITHUB_RELEASE_CDN=" +) + +set "MILL_REPO_URL=https://github.com/com-lihaoyi/mill" + +rem %~1% removes surrounding quotes +if [%~1%]==[--mill-version] ( + if not [%~2%]==[] ( + set MILL_VERSION=%~2% + rem shift command doesn't work within parentheses + set "STRIP_VERSION_PARAMS=true" + ) else ( + echo You specified --mill-version without a version. 1>&2 + echo Please provide a version that matches one provided on 1>&2 + echo %MILL_REPO_URL%/releases 1>&2 + exit /b 1 + ) +) + +if not defined STRIP_VERSION_PARAMS GOTO AfterStripVersionParams +rem strip the: --mill-version {version} +shift +shift +:AfterStripVersionParams + +if [!MILL_VERSION!]==[] ( + if exist .mill-version ( + set /p MILL_VERSION=<.mill-version + ) else ( + if exist .config\mill-version ( + set /p MILL_VERSION=<.config\mill-version + ) + ) +) + +if [!MILL_VERSION!]==[] ( + set MILL_VERSION=%DEFAULT_MILL_VERSION% +) + +set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download + +rem without bat file extension, cmd doesn't seem to be able to run it +set MILL=%MILL_DOWNLOAD_PATH%\!MILL_VERSION!.bat + +if not exist "%MILL%" ( + set VERSION_PREFIX=%MILL_VERSION:~0,4% + set DOWNLOAD_SUFFIX=-assembly + if [!VERSION_PREFIX!]==[0.0.] set DOWNLOAD_SUFFIX= + if [!VERSION_PREFIX!]==[0.1.] set DOWNLOAD_SUFFIX= + if [!VERSION_PREFIX!]==[0.2.] set DOWNLOAD_SUFFIX= + if [!VERSION_PREFIX!]==[0.3.] set DOWNLOAD_SUFFIX= + if [!VERSION_PREFIX!]==[0.4.] set DOWNLOAD_SUFFIX= + set VERSION_PREFIX= + + for /F "delims=- tokens=1" %%A in ("!MILL_VERSION!") do set MILL_VERSION_BASE=%%A + for /F "delims=- tokens=2" %%A in ("!MILL_VERSION!") do set MILL_VERSION_MILESTONE=%%A + set VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1! + if [!VERSION_MILESTONE_START!]==[M] ( + set MILL_VERSION_TAG="!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE!" + ) else ( + set MILL_VERSION_TAG=!MILL_VERSION_BASE! + ) + + rem there seems to be no way to generate a unique temporary file path (on native Windows) + set DOWNLOAD_FILE=%MILL%.tmp + + set DOWNLOAD_URL=!GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!DOWNLOAD_SUFFIX! + + echo Downloading mill %MILL_VERSION% from !DOWNLOAD_URL! ... 1>&2 + + if not exist "%MILL_DOWNLOAD_PATH%" mkdir "%MILL_DOWNLOAD_PATH%" + rem curl is bundled with recent Windows 10 + rem but I don't think we can expect all the users to have it in 2019 + where /Q curl + if %ERRORLEVEL% EQU 0 ( + curl -f -L "!DOWNLOAD_URL!" -o "!DOWNLOAD_FILE!" + ) else ( + rem bitsadmin seems to be available on Windows 7 + rem without /dynamic, github returns 403 + rem bitsadmin is sometimes needlessly slow but it looks better with /priority foreground + bitsadmin /transfer millDownloadJob /dynamic /priority foreground "!DOWNLOAD_URL!" "!DOWNLOAD_FILE!" + ) + if not exist "!DOWNLOAD_FILE!" ( + echo Could not download mill %MILL_VERSION% 1>&2 + exit /b 1 + ) + + move /y "!DOWNLOAD_FILE!" "%MILL%" + + set DOWNLOAD_FILE= + set DOWNLOAD_SUFFIX= +) + +set MILL_DOWNLOAD_PATH= +set MILL_VERSION= +set MILL_REPO_URL= + +if [!MILL_MAIN_CLI!]==[] ( + set "MILL_MAIN_CLI=%0" +) + +rem Need to preserve the first position of those listed options +set MILL_FIRST_ARG= +if [%~1%]==[--bsp] ( + set MILL_FIRST_ARG=%1% +) else ( + if [%~1%]==[-i] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--interactive] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--no-server] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--repl] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--help] ( + set MILL_FIRST_ARG=%1% + ) + ) + ) + ) + ) +) + +set "MILL_PARAMS=%*%" + +if not [!MILL_FIRST_ARG!]==[] ( + if defined STRIP_VERSION_PARAMS ( + for /f "tokens=1-3*" %%a in ("%*") do ( + set "MILL_PARAMS=%%d" + ) + ) else ( + for /f "tokens=1*" %%a in ("%*") do ( + set "MILL_PARAMS=%%b" + ) + ) +) else ( + if defined STRIP_VERSION_PARAMS ( + for /f "tokens=1-2*" %%a in ("%*") do ( + rem strip %%a - It's the "--mill-version" option. + rem strip %%b - it's the version number that comes after the option. + rem keep %%c - It's the remaining options. + set "MILL_PARAMS=%%c" + ) + ) +) + +"%MILL%" %MILL_FIRST_ARG% -D "mill.main.cli=%MILL_MAIN_CLI%" %MILL_PARAMS% diff --git a/frameworks/Scala/otavia/otavia-overshoot.dockerfile b/frameworks/Scala/otavia/otavia-overshoot.dockerfile new file mode 100644 index 00000000000..78a03840454 --- /dev/null +++ b/frameworks/Scala/otavia/otavia-overshoot.dockerfile @@ -0,0 +1,17 @@ +FROM nightscape/scala-mill:eclipse-temurin-17.0.8.1_1-jdk-focal_0.11.6_3.3.0 +WORKDIR /otavia +COPY benchmark benchmark +COPY build.sc build.sc +ENV COURSIER_REPOSITORIES=ivy2Local|central +RUN mill benchmark.assembly + +EXPOSE 8080 + +CMD java -server \ + -Dcc.otavia.actor.worker.size=64 \ + -Dcc.otavia.buffer.page.size=8 \ + -Dio.netty5.noKeySetOptimization=true \ + -jar \ + out/benchmark/assembly.dest/out.jar \ + jdbc:postgresql://tfb-database:5432/hello_world \ + benchmarkdbuser benchmarkdbpass 64 diff --git a/frameworks/Scala/otavia/otavia.dockerfile b/frameworks/Scala/otavia/otavia.dockerfile new file mode 100644 index 00000000000..f1bbd33234c --- /dev/null +++ b/frameworks/Scala/otavia/otavia.dockerfile @@ -0,0 +1,17 @@ +FROM nightscape/scala-mill:eclipse-temurin-17.0.8.1_1-jdk-focal_0.11.6_3.3.0 +WORKDIR /otavia +COPY benchmark benchmark +COPY build.sc build.sc +ENV COURSIER_REPOSITORIES=ivy2Local|central +RUN mill benchmark.assembly + +EXPOSE 8080 + +CMD java -server \ + -Dcc.otavia.actor.worker.size=56 \ + -Dcc.otavia.buffer.page.size=8 \ + -Dio.netty5.noKeySetOptimization=true \ + -jar \ + out/benchmark/assembly.dest/out.jar \ + jdbc:postgresql://tfb-database:5432/hello_world \ + benchmarkdbuser benchmarkdbpass 56 diff --git a/frameworks/Scala/vertx-web-scala/benchmark_config.json b/frameworks/Scala/vertx-web-scala/benchmark_config.json index d5f73751a49..4a8c4242d34 100755 --- a/frameworks/Scala/vertx-web-scala/benchmark_config.json +++ b/frameworks/Scala/vertx-web-scala/benchmark_config.json @@ -8,6 +8,7 @@ "fortune_url": "/fortunes", "update_url": "/updates?queries=", "plaintext_url": "/plaintext", + "json_url": "/json", "port": 8080, "approach": "Realistic", "classification": "Micro", diff --git a/frameworks/Scala/vertx-web-scala/src/main/scala/vertx/App.scala b/frameworks/Scala/vertx-web-scala/src/main/scala/vertx/App.scala index 469ba0512d6..0f2991c3b47 100644 --- a/frameworks/Scala/vertx-web-scala/src/main/scala/vertx/App.scala +++ b/frameworks/Scala/vertx-web-scala/src/main/scala/vertx/App.scala @@ -26,7 +26,7 @@ import scala.util.{Failure, Sorting, Success, Try} case class Header(name: CharSequence, value: String) class App extends ScalaVerticle { - + import App._ private val HELLO_WORLD = "Hello, world!" private val HELLO_WORLD_BUFFER = Buffer.buffer(HELLO_WORLD, "UTF-8") private val SERVER = "vert.x" @@ -116,8 +116,7 @@ class App extends ScalaVerticle { .end(World(row.getInteger(0), row.getInteger(1)).encode()) } } else { - App.logger.error("Failed to handle request", ar.cause()) - request.response.setStatusCode(500).end(ar.cause.getMessage) + sendError(request, ar.cause, "Failed to handle Db request") } } ) @@ -136,7 +135,7 @@ class App extends ScalaVerticle { if (!failed) { if (ar.failed) { failed = true - request.response.setStatusCode(500).end(ar.cause.getMessage) + sendError(request, ar.cause, "Failed to handle Queries request") return } // we need a final reference @@ -155,11 +154,6 @@ class App extends ScalaVerticle { } private def handleUpdates(request: HttpServerRequest): Unit = { - def sendError(err: Throwable): Unit = { - App.logger.error("", err) - request.response.setStatusCode(500).end(err.getMessage) - } - def handleUpdates(conn: SqlConnection, worlds: Array[World]): Unit = { Sorting.quickSort(worlds) @@ -170,7 +164,7 @@ class App extends ScalaVerticle { batch, (ar: AsyncResult[RowSet[Row]]) => { if (ar.failed) { - sendError(ar.cause) + sendError(request, ar.cause, "handleUpdates: failed to update DB") return } @@ -197,7 +191,7 @@ class App extends ScalaVerticle { if (!failed) { if (ar2.failed) { failed = true - sendError(ar2.cause) + sendError(request, ar2.cause, "handleUpdates: failed to read DB") return } worlds(index) = World(ar2.result.iterator.next.getInteger(0), App.randomWorld()) @@ -206,7 +200,6 @@ class App extends ScalaVerticle { } } ) - i += 1 } } @@ -230,9 +223,7 @@ class App extends ScalaVerticle { responseWithHeaders(request.response, contentTypeHtml) .end(html.fortune(fortunes).body) } else { - val err = ar.cause - App.logger.error("", err) - response.setStatusCode(500).end(err.getMessage) + sendError(request, ar.cause, "handleFortunes failed to update DB") } } ) @@ -241,9 +232,9 @@ class App extends ScalaVerticle { object App { val logger: Logger = Logger[App] - + val defaultConfigPath = "src/main/conf/config.json" def main(args: Array[String]): Unit = { - val config = new JsonObject(Files.readString(new File(args(0)).toPath)) + val config = new JsonObject(Files.readString(new File(if(args.length < 1) defaultConfigPath else args(0)).toPath)) val vertx = Vertx.vertx(VertxOptions().setPreferNativeTransport(true)) printConfig(vertx) @@ -299,4 +290,9 @@ object App { logger.info("Event Loop Size: {}", JVertxOptions.DEFAULT_EVENT_LOOP_POOL_SIZE) logger.info("Native transport: {}", vertx.isNativeTransportEnabled) } + + def sendError(request: HttpServerRequest, err: Throwable, msg: String = ""): Unit = { + App.logger.error(msg, err) + request.response.setStatusCode(500).end(err.getMessage) + } } diff --git a/frameworks/Scala/zio-http/build.sbt b/frameworks/Scala/zio-http/build.sbt index 987ee8cbbd2..a985d9b5dc9 100644 --- a/frameworks/Scala/zio-http/build.sbt +++ b/frameworks/Scala/zio-http/build.sbt @@ -1,13 +1,14 @@ name := "zio-http" version := "1.0.0" -scalaVersion := "2.13.6" +scalaVersion := "2.13.14" lazy val root = (project in file(".")) .settings( - libraryDependencies ++= - Seq( - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.9.1", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.9.1" % "compile-internal", - "io.d11" % "zhttp" % "1.0.0-RC5", - ), + libraryDependencies += "dev.zio" %% "zio-http" % "3.0.0-RC10", testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"), + assembly / assemblyMergeStrategy := { + case x if x.contains("io.netty.versions.properties") => MergeStrategy.discard + case x => + val oldStrategy = (assembly / assemblyMergeStrategy).value + oldStrategy(x) + } ) diff --git a/frameworks/Scala/zio-http/project/build.properties b/frameworks/Scala/zio-http/project/build.properties index 215ddd2b39d..ee06c398644 100644 --- a/frameworks/Scala/zio-http/project/build.properties +++ b/frameworks/Scala/zio-http/project/build.properties @@ -1 +1 @@ -sbt.version = 1.5.5 \ No newline at end of file +sbt.version = 1.10.0 \ No newline at end of file diff --git a/frameworks/Scala/zio-http/project/plugins.sbt b/frameworks/Scala/zio-http/project/plugins.sbt index 585d1930dc6..ec25e7aa776 100644 --- a/frameworks/Scala/zio-http/project/plugins.sbt +++ b/frameworks/Scala/zio-http/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.0.0") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.0") diff --git a/frameworks/Scala/zio-http/src/main/scala/Main.scala b/frameworks/Scala/zio-http/src/main/scala/Main.scala index b72da4e089c..8af102fd7f6 100644 --- a/frameworks/Scala/zio-http/src/main/scala/Main.scala +++ b/frameworks/Scala/zio-http/src/main/scala/Main.scala @@ -1,42 +1,43 @@ -import zhttp.http._ -import zhttp.service.Server -import zio.{App, ExitCode, URIO} -import com.github.plokhotnyuk.jsoniter_scala.macros._ -import com.github.plokhotnyuk.jsoniter_scala.core._ -import zhttp.http.Response - -import java.time.format.DateTimeFormatter -import java.time.{Instant, ZoneOffset} - -case class Message(message: String) - -object Main extends App { - val message: String = "Hello, World!" - implicit val codec: JsonValueCodec[Message] = JsonCodecMaker.make - - val app: Http[Any, HttpError, Request, Response] = Http.collect[Request] { - case Method.GET -> Root / "plaintext" => - Response.http( - content = HttpContent.Complete(message), - headers = Header.contentTypeTextPlain :: headers(), - ) - case Method.GET -> Root / "json" => - Response.http( - content = HttpContent.Complete(writeToString(Message(message))), - headers = Header.contentTypeJson :: headers(), - ) - } - - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = Server.start(8080, app).exitCode - - val formatter: DateTimeFormatter = DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC) - val constantHeaders: List[Header] = Header("server", "zio-http") :: Nil - @volatile var lastHeaders: (Long, List[Header]) = (0, Nil) - - def headers(): List[Header] = { - val t = System.currentTimeMillis() - if (t - lastHeaders._1 >= 1000) - lastHeaders = (t, Header("date", formatter.format(Instant.ofEpochMilli(t))) :: constantHeaders) - lastHeaders._2 - } -} +import zio._ +import zio.http._ +import zio.http.netty.NettyConfig +import zio.http.netty.NettyConfig.LeakDetectionLevel +import java.lang.{Runtime => JRuntime} + +object Main extends ZIOAppDefault { + + private val plainTextMessage: String = "hello, world!" + private val jsonMessage: String = """{"message": "hello, world!"}""" + + private val STATIC_SERVER_NAME = "zio-http" + private val NUM_PROCESSORS = JRuntime.getRuntime.availableProcessors() + + val app: Routes[Any, Response] = Routes( + Method.GET / "/plaintext" -> + Handler.fromResponse( + Response + .text(plainTextMessage) + .addHeader(Header.Server(STATIC_SERVER_NAME)), + ), + Method.GET / "/json" -> + Handler.fromResponse( + Response + .json(jsonMessage) + .addHeader(Header.Server(STATIC_SERVER_NAME)), + ), + ) + + private val config = Server.Config.default + .port(8080) + .enableRequestStreaming + + private val nettyConfig = NettyConfig.default + .leakDetection(LeakDetectionLevel.DISABLED) + .maxThreads(NUM_PROCESSORS) + + private val configLayer = ZLayer.succeed(config) + private val nettyConfigLayer = ZLayer.succeed(nettyConfig) + + val run: UIO[ExitCode] = + Server.serve(app).provide(configLayer, nettyConfigLayer, Server.customized).exitCode +} \ No newline at end of file diff --git a/frameworks/Swift/hummingbird2/src-postgres/Sources/server/Controllers/FortunesController.swift b/frameworks/Swift/hummingbird2/src-postgres/Sources/server/Controllers/FortunesController.swift index 159901dda40..cd8012c6d26 100644 --- a/frameworks/Swift/hummingbird2/src-postgres/Sources/server/Controllers/FortunesController.swift +++ b/frameworks/Swift/hummingbird2/src-postgres/Sources/server/Controllers/FortunesController.swift @@ -4,7 +4,7 @@ import PostgresNIO struct HTML: ResponseGenerator, Sendable { let html: String - public func response(from request: Request, context: some BaseRequestContext) -> Response { + public func response(from request: Request, context: some RequestContext) -> Response { let buffer = context.allocator.buffer(string: html) return Response(status: .ok, headers: [.contentType: "text/html; charset=utf-8"], body: .init(byteBuffer: buffer)) } diff --git a/frameworks/Swift/hummingbird2/src-postgres/Sources/server/main.swift b/frameworks/Swift/hummingbird2/src-postgres/Sources/server/main.swift index 65e360bfab3..772a52a5430 100644 --- a/frameworks/Swift/hummingbird2/src-postgres/Sources/server/main.swift +++ b/frameworks/Swift/hummingbird2/src-postgres/Sources/server/main.swift @@ -1,3 +1,4 @@ +import Foundation import Hummingbird import PostgresNIO @@ -15,15 +16,21 @@ struct TechFrameworkRequestContext: RequestContext { static let jsonEncoder = JSONEncoder() static let jsonDecoder = JSONDecoder() - var coreContext: Hummingbird.CoreRequestContext + var coreContext: Hummingbird.CoreRequestContextStorage // Use a global JSON Encoder var responseEncoder: JSONEncoder { Self.jsonEncoder } // Use a global JSON Decoder var requestDecoder: JSONDecoder { Self.jsonDecoder } + init(source: ApplicationRequestContextSource) { + self.init(channel: source.channel, logger: source.logger) + } + init(channel: any Channel, logger: Logger) { - self.coreContext = .init(allocator: channel.allocator, logger: logger) + self.coreContext = CoreRequestContextStorage( + source: ApplicationRequestContextSource(channel: channel, logger: logger) + ) } } diff --git a/frameworks/Swift/hummingbird2/src/Sources/server/main.swift b/frameworks/Swift/hummingbird2/src/Sources/server/main.swift index f652046a166..365b9a6a58f 100644 --- a/frameworks/Swift/hummingbird2/src/Sources/server/main.swift +++ b/frameworks/Swift/hummingbird2/src/Sources/server/main.swift @@ -1,4 +1,6 @@ +import Foundation import Hummingbird +import HummingbirdCore import Logging import NIOCore @@ -7,18 +9,23 @@ struct Object: ResponseEncodable { } struct TechFrameworkRequestContext: RequestContext { + static let jsonEncoder = JSONEncoder() static let jsonDecoder = JSONDecoder() - var coreContext: Hummingbird.CoreRequestContext + var coreContext: Hummingbird.CoreRequestContextStorage // Use a global JSON Encoder var responseEncoder: JSONEncoder { Self.jsonEncoder } // Use a global JSON Decoder var requestDecoder: JSONDecoder { Self.jsonDecoder } + init(source: Hummingbird.ApplicationRequestContextSource) { + self.coreContext = CoreRequestContextStorage(source: ApplicationRequestContextSource(channel: source.channel, logger: source.logger)) + } + init(channel: any Channel, logger: Logger) { - self.coreContext = .init(allocator: channel.allocator, logger: logger) + self.coreContext = CoreRequestContextStorage(source: ApplicationRequestContextSource(channel: channel, logger: logger)) } } diff --git a/frameworks/Swift/vapor/benchmark_config.json b/frameworks/Swift/vapor/benchmark_config.json index 1bd0d8e2242..00b6bb943dc 100755 --- a/frameworks/Swift/vapor/benchmark_config.json +++ b/frameworks/Swift/vapor/benchmark_config.json @@ -93,6 +93,29 @@ "display_name": "Vapor", "notes": "", "versus": "None" + }, + "swifql": { + "plaintext_url": "/plaintext", + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", + "database": "Postgres", + "orm": "Micro", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "framework": "Vapor", + "language": "Swift", + "flavor": "None", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Vapor", + "notes": "", + "versus": "None" } }] } diff --git a/frameworks/Swift/vapor/config.toml b/frameworks/Swift/vapor/config.toml index e629702ef66..24db8e89c90 100644 --- a/frameworks/Swift/vapor/config.toml +++ b/frameworks/Swift/vapor/config.toml @@ -51,3 +51,20 @@ orm = "Micro" platform = "None" webserver = "None" versus = "None" + +[swifql] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Fullstack" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Micro" +platform = "None" +webserver = "None" +versus = "None" \ No newline at end of file diff --git a/frameworks/Swift/vapor/vapor-swifql.dockerfile b/frameworks/Swift/vapor/vapor-swifql.dockerfile new file mode 100644 index 00000000000..d39373d63a6 --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql.dockerfile @@ -0,0 +1,30 @@ +# ================================ +# Build image +# ================================ +FROM swift:5.10 as build +WORKDIR /build + +# Copy entire repo into container +COPY ./vapor-swifql . + +# Compile with optimizations +RUN swift build \ + -c release \ + -Xswiftc -enforce-exclusivity=unchecked + +# ================================ +# Run image +# ================================ +FROM swift:5.10-slim +WORKDIR /run + +# Copy build artifacts +COPY --from=build /build/.build/release /run +COPY ./vapor-swifql/Resources/Views/fortune.leaf /run/Resources/Views/fortune.leaf + +# Copy Swift runtime libraries +COPY --from=build /usr/lib/swift/ /usr/lib/swift/ + +EXPOSE 8080 + +ENTRYPOINT ["./app", "serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"] diff --git a/frameworks/Swift/vapor/vapor-swifql/.dockerignore b/frameworks/Swift/vapor/vapor-swifql/.dockerignore new file mode 100644 index 00000000000..2d9f16e2d27 --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/.dockerignore @@ -0,0 +1,2 @@ +.build/ +.swiftpm/ diff --git a/frameworks/Swift/vapor/vapor-swifql/.gitignore b/frameworks/Swift/vapor/vapor-swifql/.gitignore new file mode 100644 index 00000000000..1f4d514fd4c --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/.gitignore @@ -0,0 +1,14 @@ +Packages +.build +xcuserdata +*.xcodeproj +DerivedData/ +.DS_Store +db.sqlite +.swiftpm +.env +.env.* +! .env.example +.vscode +docker-compose.yml +Dockerfile \ No newline at end of file diff --git a/frameworks/Swift/vapor/vapor-swifql/Package.swift b/frameworks/Swift/vapor/vapor-swifql/Package.swift new file mode 100644 index 00000000000..43d9104cecd --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/Package.swift @@ -0,0 +1,46 @@ +// swift-tools-version:5.10 + +import PackageDescription + +let package = Package( + name: "vapor-swifql-ikiga", + platforms: [ + .macOS(.v12) + ], + products: [ + .executable(name: "app", targets: ["App"]) + ], + dependencies: [ + // 💧 A server-side Swift web framework. + .package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"), + .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"), + // 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors + .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"), + // json encoder/decoder + .package(url: "https://github.com/orlandos-nl/IkigaJSON.git", from: "2.0.0"), + // sql builder + .package(url: "https://github.com/SwifQL/VaporBridges.git", from: "1.0.0-rc"), + .package(url: "https://github.com/SwifQL/PostgresBridge.git", from: "1.0.0-rc"), + ], + targets: [ + .executableTarget( + name: "App", + dependencies: [ + .product(name: "Vapor", package: "vapor"), + .product(name: "Leaf", package: "leaf"), + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "VaporBridges", package: "VaporBridges"), + .product(name: "PostgresBridge", package: "PostgresBridge"), + .product(name: "IkigaJSON", package: "IkigaJSON"), + ], + swiftSettings: swiftSettings + ) + ] +) + +var swiftSettings: [SwiftSetting] { [ + .enableUpcomingFeature("DisableOutwardActorInference"), + .enableExperimentalFeature("StrictConcurrency"), + .unsafeFlags(["-parse-as-library"]), +] } diff --git a/frameworks/Swift/vapor/vapor-swifql/Resources/Views/fortune.leaf b/frameworks/Swift/vapor/vapor-swifql/Resources/Views/fortune.leaf new file mode 100644 index 00000000000..020d76adc92 --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/Resources/Views/fortune.leaf @@ -0,0 +1,10 @@ + + +Fortunes + + + +#for(fortune in fortunes): +#endfor
idmessage
#(fortune.id)#(fortune.message)
+ + \ No newline at end of file diff --git a/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/IkigaJSONCoders+ContentCoders.swift b/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/IkigaJSONCoders+ContentCoders.swift new file mode 100644 index 00000000000..676ff7285b6 --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/IkigaJSONCoders+ContentCoders.swift @@ -0,0 +1,56 @@ +// +// IkigaJSONCoders+ContentCoders.swift +// +// +// Created by Yakov Shapovalov on 04.07.2024. +// + +import IkigaJSON +import Vapor + +extension IkigaJSONEncoder: ContentEncoder { + public func encode( + _ encodable: E, + to body: inout ByteBuffer, + headers: inout HTTPHeaders + ) throws { + headers.contentType = .json + try self.encodeAndWrite(encodable, into: &body) + } + + public func encode(_ encodable: E, to body: inout ByteBuffer, headers: inout HTTPHeaders, userInfo: [CodingUserInfoKey : Sendable]) throws where E : Encodable { + var encoder = self + encoder.userInfo = userInfo + headers.contentType = .json + try encoder.encodeAndWrite(encodable, into: &body) + } + + public func encode(_ encodable: E, to body: inout ByteBuffer, headers: inout HTTPHeaders, userInfo: [CodingUserInfoKey : Any]) throws where E : Encodable { + var encoder = self + encoder.userInfo = userInfo + headers.contentType = .json + try encoder.encodeAndWrite(encodable, into: &body) + } +} + +extension IkigaJSONDecoder: ContentDecoder { + public func decode( + _ decodable: D.Type, + from body: ByteBuffer, + headers: HTTPHeaders + ) throws -> D { + return try self.decode(D.self, from: body) + } + + public func decode(_ decodable: D.Type, from body: ByteBuffer, headers: HTTPHeaders, userInfo: [CodingUserInfoKey : Sendable]) throws -> D where D : Decodable { + let decoder = IkigaJSONDecoder(settings: settings) + decoder.settings.userInfo = userInfo + return try decoder.decode(D.self, from: body) + } + + public func decode(_ decodable: D.Type, from body: ByteBuffer, headers: HTTPHeaders, userInfo: [CodingUserInfoKey : Any]) throws -> D where D : Decodable { + let decoder = IkigaJSONDecoder(settings: settings) + decoder.settings.userInfo = userInfo + return try decoder.decode(D.self, from: body) + } +} diff --git a/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/Models+Content.swift b/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/Models+Content.swift new file mode 100644 index 00000000000..8fdae4b378f --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/Models+Content.swift @@ -0,0 +1,11 @@ +// +// Models+Content.swift +// +// +// Created by Yakov Shapovalov on 04.07.2024. +// + +import Vapor + +extension World: Content {} +extension Fortune: Content {} diff --git a/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/Utils.swift b/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/Utils.swift new file mode 100644 index 00000000000..d370b15e291 --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/Sources/Extensions/Utils.swift @@ -0,0 +1,18 @@ +extension Int { + func bounded(to range: ClosedRange) -> Int { + switch self { + case ...range.lowerBound: + return range.lowerBound + case range.upperBound...: + return range.upperBound + default: + return self + } + } +} + +extension Int: Sequence { + public func makeIterator() -> CountableRange.Iterator { + return (0.. World in + guard let world: World = try await req.postgres.connection(to: .Db, { conn in + World.select + .where(\World.$id == Int.random(in: 1...10_000)) + .execute(on: conn) + .first(decoding: World.self) + }).get() else { + throw Abort(.notFound) + } + return world + } + + app.get("queries") { req async throws -> [World] in + let queries: Int = (req.query["queries"] ?? 1).bounded(to: 1...500) + + var worlds: [World] = [] + + for _ in queries { + guard let world: World = try await req.postgres.connection(to: .Db, { conn in + World.select + .where(\World.$id == Int.random(in: 1...10_000)) + .execute(on: conn) + .first(decoding: World.self) + }).get() else { + throw Abort(.notFound) + } + + worlds.append(world) + } + return worlds + } + + app.get("updates") { req async throws -> [World] in + let queries = (req.query["queries"] ?? 1).bounded(to: 1...500) + + var worlds: [World] = [] + + for _ in queries { + let world = try await req.postgres.connection(to: .Db, { conn in + World.select.where(\World.$id == Int.random(in: 1...10_000)).execute(on: conn).first(decoding: World.self).flatMap { world in + world!.randomnumber = .random(in: 1...10_000) + return world!.update(on: \.$id, on: conn) + } + }).get() + + worlds.append(world) + } + + return worlds + + } + + app.get("fortunes") { req async throws -> View in + var fortunes: [Fortune] = try await req.postgres.connection(to: .Db, {conn in + Fortune.select.execute(on: conn).all(decoding: Fortune.self) + }) + .get() + + fortunes.append(Fortune(id: 0, message: "Additional fortune added at request time.")) + + fortunes.sort(by: { + $0.message < $1.message + }) + + return try await req.view.render("fortune", ["fortunes": fortunes]) + } +} + diff --git a/frameworks/Swift/vapor/vapor-swifql/Sources/main.swift b/frameworks/Swift/vapor/vapor-swifql/Sources/main.swift new file mode 100644 index 00000000000..81feddfbc02 --- /dev/null +++ b/frameworks/Swift/vapor/vapor-swifql/Sources/main.swift @@ -0,0 +1,16 @@ +import Vapor +import PostgresBridge +import Logging + +@main +enum App { + static func main() throws { + var env = try Environment.detect() + try LoggingSystem.bootstrap(from: &env) + + let app = Application(env) + defer { app.shutdown() } + + try configure(app) + } +} diff --git a/frameworks/TypeScript/bun/bun.dockerfile b/frameworks/TypeScript/bun/bun.dockerfile index 7692c8c82ae..ecf6ead2bbc 100644 --- a/frameworks/TypeScript/bun/bun.dockerfile +++ b/frameworks/TypeScript/bun/bun.dockerfile @@ -1,13 +1,15 @@ -FROM oven/bun:1.0 +FROM oven/bun:1.1 EXPOSE 8080 WORKDIR /app -USER bun - COPY ./src . ENV NODE_ENV=production +RUN bun build --compile --minify --outfile server . + +USER bun + CMD ["bun", "spawn.ts"] diff --git a/frameworks/TypeScript/bun/src/index.ts b/frameworks/TypeScript/bun/src/index.ts index be72a268363..45c0f303291 100644 --- a/frameworks/TypeScript/bun/src/index.ts +++ b/frameworks/TypeScript/bun/src/index.ts @@ -1,5 +1,5 @@ -const HELLO_WORLD_STR = "Hello, World!"; -const options: ResponseInit = { headers: { "Server": "Bun" } }; +const plainOptions: ResponseInit = { headers: { "Server": "Bun" } }; +const jsonOptions: ResponseInit = { headers: { "Server": "Bun", "Content-Type": "application/json" } }; const server = Bun.serve({ port: 8080, @@ -7,16 +7,16 @@ const server = Bun.serve({ fetch(req: Request) { const pathname = req.url.slice(req.url.indexOf("/", 8)); - if (pathname === "/json") { - return Response.json({ message: HELLO_WORLD_STR }, options); + if (pathname == "/json") { + return new Response(JSON.stringify({ message: "Hello, World!" }), jsonOptions); } - if (pathname === "/plaintext") { - return new Response(HELLO_WORLD_STR, options); + if (pathname == "/plaintext") { + return new Response("Hello, World!", plainOptions); } return new Response("", { status: 404 }) }, }); -console.log(`Listening on localhost:${server.port}`); +console.log(`Listening on ${server.url}\n`); diff --git a/frameworks/TypeScript/bun/src/spawn.ts b/frameworks/TypeScript/bun/src/spawn.ts index b3df7f03253..a6d990c1b67 100644 --- a/frameworks/TypeScript/bun/src/spawn.ts +++ b/frameworks/TypeScript/bun/src/spawn.ts @@ -1,9 +1,18 @@ -import os from "node:os"; +const cpus = navigator.hardwareConcurrency; +const buns = new Array(cpus); -const numCPUs = os.cpus().length; -for (let i = 0; i < numCPUs; i++) { - Bun.spawn(["bun", "index.ts"], { +for (let i = 0; i < cpus; i++) { + buns[i] = Bun.spawn(["./server"], { stdio: ["inherit", "inherit", "inherit"], env: { ...process.env }, }); } + +function kill() { + for (const bun of buns) { + bun.kill(); + } +} + +process.on("SIGINT", kill); +process.on("exit", kill); \ No newline at end of file diff --git a/frameworks/TypeScript/deno/deno.dockerfile b/frameworks/TypeScript/deno/deno.dockerfile index bd98d179541..cfb43644c10 100644 --- a/frameworks/TypeScript/deno/deno.dockerfile +++ b/frameworks/TypeScript/deno/deno.dockerfile @@ -1,4 +1,4 @@ -FROM denoland/deno:1.42.1 +FROM denoland/deno:1.46.1 EXPOSE 8080 @@ -12,4 +12,4 @@ RUN deno cache main.ts EXPOSE 8080 -CMD ["run", "-A", "--unstable-net", "spawn.ts"] +CMD ["deno", "serve", "--parallel", "--port", "8080", "--host", "0.0.0.0", "-A", "main.ts"] diff --git a/frameworks/TypeScript/deno/src/main.ts b/frameworks/TypeScript/deno/src/main.ts index 347e7f4c1a8..3e6f71506bd 100644 --- a/frameworks/TypeScript/deno/src/main.ts +++ b/frameworks/TypeScript/deno/src/main.ts @@ -1,9 +1,8 @@ const HELLO_WORLD_STR = "Hello, World!"; const options: ResponseInit = { headers: { "Server": "Deno" } }; -Deno.serve({ - reusePort: true, - handler: (req: Request) => { +export default { + fetch: (req: Request) => { const path = req.url.slice(req.url.indexOf("/", 8)); if (path == "/plaintext") { return new Response(HELLO_WORLD_STR, options); @@ -13,10 +12,4 @@ Deno.serve({ return new Response("404 Not Found", { status: 404, ...options }); } }, - onError(err) { - console.error(err); - Deno.exit(9); - }, - port: 8080, - hostname: "0.0.0.0", -}); +}; diff --git a/frameworks/TypeScript/deno/src/spawn.ts b/frameworks/TypeScript/deno/src/spawn.ts deleted file mode 100644 index cc56c4543b6..00000000000 --- a/frameworks/TypeScript/deno/src/spawn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import os from "node:os"; -import process from "node:process"; - -const numCPUs = os.cpus().length; -for (let i = 0; i < numCPUs; i++) { - new Deno.Command(Deno.execPath(), { - args: ["run", "-A", "--unstable-net", "main.ts"], - stdin: "inherit", - stdout: "inherit", - stderr: "inherit", - env: { ...process.env }, - }).spawn(); -} diff --git a/frameworks/TypeScript/ditsmod/.gitignore b/frameworks/TypeScript/ditsmod/.gitignore index b7c122d83c3..d525fa34c07 100644 --- a/frameworks/TypeScript/ditsmod/.gitignore +++ b/frameworks/TypeScript/ditsmod/.gitignore @@ -5,3 +5,4 @@ dist* .pnp* nodemon.json package-lock.json +bun.lockb diff --git a/frameworks/TypeScript/ditsmod/benchmark_config.json b/frameworks/TypeScript/ditsmod/benchmark_config.json index 42122a11d69..11704b96a53 100755 --- a/frameworks/TypeScript/ditsmod/benchmark_config.json +++ b/frameworks/TypeScript/ditsmod/benchmark_config.json @@ -3,6 +3,7 @@ "tests": [ { "default": { + "dockerfile": "ditsmod.dockerfile", "json_url": "/json", "plaintext_url": "/plaintext", "port": 8080, @@ -17,31 +18,12 @@ "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "Ditsmod", - "notes": "", - "versus": "nodejs" - }, - "simplified-di": { - "dockerfile": "ditsmod.dockerfile", - "json_url": "/json2", - "plaintext_url": "/plaintext2", - "port": 8080, - "approach": "Realistic", - "classification": "Micro", - "database": "None", - "framework": "Ditsmod", - "language": "TypeScript", - "flavor": "None", - "orm": "None", - "platform": "nodejs", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "ditsmod [simplified use of di]", + "display_name": "ditsmod", "notes": "Simplified use of Dependency Injection (no request level injector).", "versus": "nodejs" }, "postgres": { + "dockerfile": "ditsmod-postgres.dockerfile", "db_url": "/db", "query_url": "/queries?queries=", "update_url": "/updates?queries=", @@ -60,10 +42,11 @@ "os": "Linux", "database_os": "Linux", "display_name": "ditsmod [postgres]", - "notes": "", + "notes": "Simplified use of Dependency Injection (no request level injector).", "versus": "nodejs" }, "mysql": { + "dockerfile": "ditsmod-mysql.dockerfile", "db_url": "/db", "query_url": "/queries?queries=", "update_url": "/updates?queries=", @@ -82,54 +65,74 @@ "os": "Linux", "database_os": "Linux", "display_name": "ditsmod [mysql]", - "notes": "", + "notes": "Simplified use of Dependency Injection (no request level injector).", "versus": "nodejs" }, - "postgres2": { - "dockerfile": "ditsmod-postgres.dockerfile", - "db_url": "/db2", - "query_url": "/queries2?queries=", - "update_url": "/updates2?queries=", - "cached_query_url": "/cached-queries2?count=", - "fortune_url": "/fortunes2", + "bun": { + "dockerfile": "ditsmod-bun.dockerfile", + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "None", + "framework": "ditsmod-bun", + "language": "TypeScript", + "flavor": "None", + "orm": "None", + "platform": "bun", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "ditsmod on bun", + "notes": "Simplified use of Dependency Injection (no request level injector).", + "versus": "bun" + }, + "postgres-bun": { + "dockerfile": "ditsmod-bun-postgres.dockerfile", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "cached_query_url": "/cached-queries?count=", + "fortune_url": "/fortunes", "port": 8080, "approach": "Realistic", "classification": "Micro", "database": "Postgres", - "framework": "Ditsmod", + "framework": "ditsmod-bun", "language": "TypeScript", "flavor": "None", "orm": "Raw", - "platform": "nodejs", + "platform": "bun", "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "ditsmod [postgres & simplified use of di]", + "display_name": "ditsmod on bun [postgres]", "notes": "Simplified use of Dependency Injection (no request level injector).", - "versus": "nodejs" + "versus": "bun" }, - "mysql2": { - "dockerfile": "ditsmod-mysql.dockerfile", - "db_url": "/db2", - "query_url": "/queries2?queries=", - "update_url": "/updates2?queries=", - "cached_query_url": "/cached-queries2?count=", - "fortune_url": "/fortunes2", + "mysql-bun": { + "dockerfile": "ditsmod-bun-mysql.dockerfile", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "cached_query_url": "/cached-queries?count=", + "fortune_url": "/fortunes", "port": 8080, "approach": "Realistic", "classification": "Micro", "database": "MySQL", - "framework": "Ditsmod", + "framework": "ditsmod-bun", "language": "TypeScript", "flavor": "None", "orm": "Raw", - "platform": "nodejs", + "platform": "bun", "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "ditsmod [mysql & simplified use of di]", + "display_name": "ditsmod on bun [mysql]", "notes": "Simplified use of Dependency Injection (no request level injector).", - "versus": "nodejs" + "versus": "bun" } } ] diff --git a/frameworks/TypeScript/ditsmod/ditsmod-bun-mysql.dockerfile b/frameworks/TypeScript/ditsmod/ditsmod-bun-mysql.dockerfile new file mode 100644 index 00000000000..d0f44000dc7 --- /dev/null +++ b/frameworks/TypeScript/ditsmod/ditsmod-bun-mysql.dockerfile @@ -0,0 +1,16 @@ +FROM oven/bun:1.1 + +COPY ./ ./ + +RUN bun install +RUN bun run build + +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 +CMD rm node_modules/@ditsmod/*/tsconfig.json && bun dist/main.js diff --git a/frameworks/TypeScript/ditsmod/ditsmod-bun-postgres.dockerfile b/frameworks/TypeScript/ditsmod/ditsmod-bun-postgres.dockerfile new file mode 100644 index 00000000000..1bcc29a8b2f --- /dev/null +++ b/frameworks/TypeScript/ditsmod/ditsmod-bun-postgres.dockerfile @@ -0,0 +1,16 @@ +FROM oven/bun:1.1 + +COPY ./ ./ + +RUN bun install +RUN bun run build + +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 +CMD rm node_modules/@ditsmod/*/tsconfig.json && bun dist/main.js diff --git a/frameworks/TypeScript/ditsmod/ditsmod-bun.dockerfile b/frameworks/TypeScript/ditsmod/ditsmod-bun.dockerfile new file mode 100644 index 00000000000..8f0268556af --- /dev/null +++ b/frameworks/TypeScript/ditsmod/ditsmod-bun.dockerfile @@ -0,0 +1,11 @@ +FROM oven/bun:1.1 + +COPY ./ ./ + +RUN bun install +RUN bun run build + +ENV NODE_ENV production + +EXPOSE 8080 +CMD rm node_modules/@ditsmod/*/tsconfig.json && bun dist/main.js diff --git a/frameworks/TypeScript/ditsmod/ditsmod-mysql.dockerfile b/frameworks/TypeScript/ditsmod/ditsmod-mysql.dockerfile index 4136e2ae860..95ad40e9ac8 100644 --- a/frameworks/TypeScript/ditsmod/ditsmod-mysql.dockerfile +++ b/frameworks/TypeScript/ditsmod/ditsmod-mysql.dockerfile @@ -1,4 +1,4 @@ -FROM node:18.17.1-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/ditsmod/ditsmod-postgres.dockerfile b/frameworks/TypeScript/ditsmod/ditsmod-postgres.dockerfile index 5ef79595757..89ecc3e80a8 100644 --- a/frameworks/TypeScript/ditsmod/ditsmod-postgres.dockerfile +++ b/frameworks/TypeScript/ditsmod/ditsmod-postgres.dockerfile @@ -1,4 +1,4 @@ -FROM node:18.17.1-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/ditsmod/ditsmod.dockerfile b/frameworks/TypeScript/ditsmod/ditsmod.dockerfile index 882239148ce..0b21155188a 100644 --- a/frameworks/TypeScript/ditsmod/ditsmod.dockerfile +++ b/frameworks/TypeScript/ditsmod/ditsmod.dockerfile @@ -1,4 +1,4 @@ -FROM node:18.17.1-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/ditsmod/package.json b/frameworks/TypeScript/ditsmod/package.json index 962d11cc65f..a4d1ec09851 100755 --- a/frameworks/TypeScript/ditsmod/package.json +++ b/frameworks/TypeScript/ditsmod/package.json @@ -10,27 +10,23 @@ "build": "tsc -b tsconfig.build.json", "clean": "rm -rf dist*" }, - "imports": { - "#routed/*": "./dist/app/modules/routed/*", - "#service/*": "./dist/app/modules/service/*", - "#utils/*": "./dist/app/utils/*" - }, "keywords": [], "author": "Костя Третяк", "license": "MIT", "dependencies": { - "@ditsmod/core": "~2.51.1", - "@ditsmod/routing": "~2.1.0", + "@ditsmod/core": "~2.55.0", + "@ditsmod/routing": "~2.3.0", "handlebars": "^4.7.8", - "lru-cache": "^10.0.1", - "mariadb": "^3.2.1", - "postgres": "^3.3.5" + "lru-cache": "^11.0.0", + "mariadb": "^3.3.1", + "postgres": "^3.4.4" }, "devDependencies": { "@types/eslint": "^8.44.2", "@types/node": "^20.5.7", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", + "bun-types": "^1.1.22", "eslint": "^8.48.0", "prettier": "^3.0.2", "typescript": "^5.2.2" diff --git a/frameworks/TypeScript/ditsmod/src/app/app.module.ts b/frameworks/TypeScript/ditsmod/src/app/app.module.ts index 4874719f9cc..34bfc4357cf 100644 --- a/frameworks/TypeScript/ditsmod/src/app/app.module.ts +++ b/frameworks/TypeScript/ditsmod/src/app/app.module.ts @@ -1,8 +1,16 @@ import { Providers, rootModule } from '@ditsmod/core'; -import { SimpleModule } from '#routed/simple/simple.module.js'; +import { PRE_ROUTER_EXTENSIONS, RoutingModule } from '@ditsmod/routing'; + +import { OneController } from './one.controller.js'; +import { DbService } from './db.service.js'; +import { InitExtension } from './init.extension.js'; +import { DB_INIT_EXTENSIONS } from './tokens.js'; +import { ModelService } from './types.js'; @rootModule({ - appends: [SimpleModule], - providersPerApp: [...new Providers().useLogConfig({ level: 'off' })], + imports: [RoutingModule], + providersPerApp: new Providers().passThrough(DbService).passThrough(ModelService).useLogConfig({ level: 'off' }), + extensions: [{ extension: InitExtension, groupToken: DB_INIT_EXTENSIONS, nextToken: PRE_ROUTER_EXTENSIONS }], + controllers: [OneController], }) export class AppModule {} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/db.service.ts b/frameworks/TypeScript/ditsmod/src/app/db.service.ts similarity index 96% rename from frameworks/TypeScript/ditsmod/src/app/modules/service/db/db.service.ts rename to frameworks/TypeScript/ditsmod/src/app/db.service.ts index 61c9b5bbfbd..1267c11f360 100644 --- a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/db.service.ts +++ b/frameworks/TypeScript/ditsmod/src/app/db.service.ts @@ -1,8 +1,8 @@ import { injectable } from '@ditsmod/core'; import { LRUCache } from 'lru-cache'; -import { getNumberOfObjects, getRandomNumber } from '#utils/helper.js'; import { ModelService, World } from './types.js'; +import { getNumberOfObjects, getRandomNumber } from './helper.js'; @injectable() export class DbService { diff --git a/frameworks/TypeScript/ditsmod/src/app/utils/helper.ts b/frameworks/TypeScript/ditsmod/src/app/helper.ts similarity index 90% rename from frameworks/TypeScript/ditsmod/src/app/utils/helper.ts rename to frameworks/TypeScript/ditsmod/src/app/helper.ts index 33a0cb20ad6..3adcffe4fef 100644 --- a/frameworks/TypeScript/ditsmod/src/app/utils/helper.ts +++ b/frameworks/TypeScript/ditsmod/src/app/helper.ts @@ -1,4 +1,4 @@ -import { Fortune } from '#service/db/types.js'; +import { Fortune } from './types.js'; export function getRandomNumber() { return Math.floor(Math.random() * 10000) + 1; diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/init.extension.ts b/frameworks/TypeScript/ditsmod/src/app/init.extension.ts similarity index 100% rename from frameworks/TypeScript/ditsmod/src/app/modules/service/db/init.extension.ts rename to frameworks/TypeScript/ditsmod/src/app/init.extension.ts diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/db.controller.ts b/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/db.controller.ts deleted file mode 100644 index ac39c4bc715..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/db.controller.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { AnyObj, controller, inject, QUERY_PARAMS, Res, route } from '@ditsmod/core'; - -import { DbService } from '#service/db/db.service.js'; -import { getRandomNumber } from '#utils/helper.js'; - -@controller() -export class DbController { - constructor( - private res: Res, - private dbService: DbService, - ) { - res.nodeRes.setHeader('Server', 'Ditsmod'); - } - - @route('GET', 'db') - async getSingleQuery() { - const id = getRandomNumber(); - const result = await this.dbService.findOneWorld(id); - this.res.sendJson(result); - } - - @route('GET', 'queries') - async getMultiQueries(@inject(QUERY_PARAMS) queryParams: AnyObj) { - const result = await this.dbService.getMultiQueries(queryParams.queries); - this.res.sendJson(result); - } - - @route('GET', 'cached-queries') - async getCachedWorlds(@inject(QUERY_PARAMS) queryParams: AnyObj) { - const result = await this.dbService.getMultiQueries(queryParams.count, false); - this.res.sendJson(result); - } - - @route('GET', 'updates') - async getUpdates(@inject(QUERY_PARAMS) queryParams: AnyObj) { - const worlds = await this.dbService.saveWorlds(queryParams.queries); - this.res.sendJson(worlds); - } -} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/db.controller2.ts b/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/db.controller2.ts deleted file mode 100644 index cef38bd4d0b..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/db.controller2.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { AnyObj, controller, RequestContext, SingletonRequestContext, route } from '@ditsmod/core'; - -import { DbService } from '#service/db/db.service.js'; -import { getRandomNumber } from '#utils/helper.js'; - -@controller({ isSingleton: true }) -export class DbController2 { - constructor(private dbService: DbService) {} - - @route('GET', 'db2') - async getSingleQuery(ctx: RequestContext) { - const id = getRandomNumber(); - const result = await this.dbService.findOneWorld(id); - this.sendJson(ctx, result); - } - - @route('GET', 'queries2') - async getMultiQueries(ctx: SingletonRequestContext) { - const result = await this.dbService.getMultiQueries(ctx.queryParams!.queries); - this.sendJson(ctx, result); - } - - @route('GET', 'cached-queries2') - async getCachedWorlds(ctx: SingletonRequestContext) { - const result = await this.dbService.getMultiQueries(ctx.queryParams!.count, false); - this.sendJson(ctx, result); - } - - @route('GET', 'updates2') - async getUpdates(ctx: SingletonRequestContext) { - const worlds = await this.dbService.saveWorlds(ctx.queryParams!.queries); - this.sendJson(ctx, worlds); - } - - protected sendJson(ctx: RequestContext, value: AnyObj) { - ctx.nodeRes.setHeader('Server', 'Ditsmod'); - ctx.nodeRes.setHeader('Content-Type', 'application/json; charset=utf-8'); - ctx.nodeRes.end(JSON.stringify(value)); - } -} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/fortune.controller.ts b/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/fortune.controller.ts deleted file mode 100644 index 64c6b087cc5..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/fortune.controller.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Handlebars from 'handlebars'; -import { NODE_RES, NodeResponse, controller, inject, route } from '@ditsmod/core'; - -import { additionalFortune, compare } from '#utils/helper.js'; -import { DbService } from '#service/db/db.service.js'; - -const tmpl = Handlebars.compile( - [ - '', - '', - 'Fortunes', - '', - '', - '', - '', - '', - '', - '{{#fortunes}}', - '', - '', - '', - '', - '{{/fortunes}}', - '
idmessage
{{id}}{{message}}
', - '', - '', - ].join(''), -); - -@controller() -export class FortuneController { - @route('GET', 'fortunes') - async fortunes(@inject(NODE_RES) nodeRes: NodeResponse, dbService: DbService) { - const fortunes = await dbService.findAllFortunes(); - fortunes.push(additionalFortune); - fortunes.sort(compare); - nodeRes.setHeader('Server', 'Ditsmod'); - nodeRes.setHeader('Content-Type', 'text/html; charset=utf-8'); - nodeRes.end(tmpl({ fortunes })); - } -} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/fortune.controller2.ts b/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/fortune.controller2.ts deleted file mode 100644 index 5100a0dcb54..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/fortune.controller2.ts +++ /dev/null @@ -1,43 +0,0 @@ -import Handlebars from 'handlebars'; -import { RequestContext, controller, route } from '@ditsmod/core'; - -import { additionalFortune, compare } from '#utils/helper.js'; -import { DbService } from '#service/db/db.service.js'; - -const tmpl = Handlebars.compile( - [ - '', - '', - 'Fortunes', - '', - '', - '', - '', - '', - '', - '{{#fortunes}}', - '', - '', - '', - '', - '{{/fortunes}}', - '
idmessage
{{id}}{{message}}
', - '', - '', - ].join(''), -); - -@controller({ isSingleton: true }) -export class FortuneController2 { - constructor(private dbService: DbService) {} - - @route('GET', 'fortunes2') - async fortunes(ctx: RequestContext) { - const fortunes = await this.dbService.findAllFortunes(); - fortunes.push(additionalFortune); - fortunes.sort(compare); - ctx.nodeRes.setHeader('Server', 'Ditsmod'); - ctx.nodeRes.setHeader('Content-Type', 'text/html; charset=utf-8'); - ctx.nodeRes.end(tmpl({ fortunes })); - } -} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/simple.module.ts b/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/simple.module.ts deleted file mode 100644 index 123681d91aa..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/simple.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { featureModule } from '@ditsmod/core'; -import { RoutingModule } from '@ditsmod/routing'; - -import { DbModule } from '#service/db/db.module.js'; -import { WithoutDbController } from './without-db.controller.js'; -import { DbController } from './db.controller.js'; -import { FortuneController } from './fortune.controller.js'; -import { SingletonController } from './singleton.controller.js'; -import { DbController2 } from './db.controller2.js'; -import { FortuneController2 } from './fortune.controller2.js'; - -@featureModule({ - imports: [RoutingModule, DbModule], - controllers: [ - WithoutDbController, - DbController, - DbController2, - FortuneController, - FortuneController2, - SingletonController, - ], -}) -export class SimpleModule {} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/singleton.controller.ts b/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/singleton.controller.ts deleted file mode 100644 index 3661775e536..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/singleton.controller.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { controller, route, SingletonRequestContext } from '@ditsmod/core'; - -@controller({ isSingleton: true }) -export class SingletonController { - @route('GET', 'plaintext2') - getHello(ctx: SingletonRequestContext) { - ctx.nodeRes.setHeader('Server', 'Ditsmod'); - ctx.nodeRes.setHeader('Content-Type', 'text/plain; charset=utf-8'); - ctx.nodeRes.end('Hello, World!'); - } - - @route('GET', 'json2') - getJson(ctx: SingletonRequestContext) { - ctx.nodeRes.setHeader('Server', 'Ditsmod'); - ctx.nodeRes.setHeader('Content-Type', 'application/json; charset=utf-8'); - ctx.nodeRes.end(JSON.stringify({ message: 'Hello, World!' })); - } -} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/without-db.controller.ts b/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/without-db.controller.ts deleted file mode 100644 index 9ff5246609f..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/routed/simple/without-db.controller.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { controller, Res, route } from '@ditsmod/core'; - -@controller() -export class WithoutDbController { - constructor(private res: Res) { - res.nodeRes.setHeader('Server', 'Ditsmod'); - } - - @route('GET', 'plaintext') - getHello() { - this.res.send('Hello, World!'); - } - - @route('GET', 'json') - getJson() { - this.res.sendJson({ message: 'Hello, World!' }); - } -} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/db.module.ts b/frameworks/TypeScript/ditsmod/src/app/modules/service/db/db.module.ts deleted file mode 100644 index 08de21fb22a..00000000000 --- a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/db.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { featureModule } from '@ditsmod/core'; -import { PRE_ROUTER_EXTENSIONS } from '@ditsmod/routing'; - -import { DbService } from './db.service.js'; -import { InitExtension } from './init.extension.js'; -import { DB_INIT_EXTENSIONS } from './tokens.js'; -import { ModelService } from './types.js'; - -@featureModule({ - providersPerApp: [DbService, ModelService], - extensions: [{ extension: InitExtension, groupToken: DB_INIT_EXTENSIONS, nextToken: PRE_ROUTER_EXTENSIONS }], -}) -export class DbModule {} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/mysql.service.ts b/frameworks/TypeScript/ditsmod/src/app/mysql.service.ts similarity index 100% rename from frameworks/TypeScript/ditsmod/src/app/modules/service/db/mysql.service.ts rename to frameworks/TypeScript/ditsmod/src/app/mysql.service.ts diff --git a/frameworks/TypeScript/ditsmod/src/app/one.controller.ts b/frameworks/TypeScript/ditsmod/src/app/one.controller.ts new file mode 100644 index 00000000000..09615c93598 --- /dev/null +++ b/frameworks/TypeScript/ditsmod/src/app/one.controller.ts @@ -0,0 +1,88 @@ +import { AnyObj, controller, RequestContext, SingletonRequestContext, route } from '@ditsmod/core'; +import Handlebars from 'handlebars'; + +import { DbService } from './db.service.js'; +import { additionalFortune, compare, getRandomNumber } from './helper.js'; + +const tmpl = Handlebars.compile( + [ + '', + '', + 'Fortunes', + '', + '', + '', + '', + '', + '', + '{{#fortunes}}', + '', + '', + '', + '', + '{{/fortunes}}', + '
idmessage
{{id}}{{message}}
', + '', + '', + ].join(''), +); + +@controller({ isSingleton: true }) +export class OneController { + constructor(private dbService: DbService) {} + + @route('GET', 'db') + async getSingleQuery(ctx: RequestContext) { + const id = getRandomNumber(); + const result = await this.dbService.findOneWorld(id); + this.sendJson(ctx, result); + } + + @route('GET', 'queries') + async getMultiQueries(ctx: SingletonRequestContext) { + const result = await this.dbService.getMultiQueries(ctx.queryParams!.queries); + this.sendJson(ctx, result); + } + + @route('GET', 'cached-queries') + async getCachedWorlds(ctx: SingletonRequestContext) { + const result = await this.dbService.getMultiQueries(ctx.queryParams!.count, false); + this.sendJson(ctx, result); + } + + @route('GET', 'updates') + async getUpdates(ctx: SingletonRequestContext) { + const worlds = await this.dbService.saveWorlds(ctx.queryParams!.queries); + this.sendJson(ctx, worlds); + } + + @route('GET', 'fortunes') + async fortunes(ctx: RequestContext) { + const fortunes = await this.dbService.findAllFortunes(); + fortunes.push(additionalFortune); + fortunes.sort(compare); + ctx.nodeRes.setHeader('Server', 'Ditsmod'); + ctx.nodeRes.setHeader('Content-Type', 'text/html; charset=utf-8'); + ctx.nodeRes.end(tmpl({ fortunes })); + } + + @route('GET', 'plaintext') + getHello(ctx: SingletonRequestContext) { + ctx.nodeRes.setHeader('Server', 'Ditsmod'); + ctx.nodeRes.setHeader('Content-Type', 'text/plain; charset=utf-8'); + ctx.nodeRes.end('Hello, World!'); + } + + @route('GET', 'json') + getJson(ctx: SingletonRequestContext) { + ctx.nodeRes.setHeader('Server', 'Ditsmod'); + ctx.nodeRes.setHeader('Content-Type', 'application/json; charset=utf-8'); + ctx.nodeRes.end(JSON.stringify({ message: 'Hello, World!' })); + } + + protected sendJson(ctx: RequestContext, value: AnyObj) { + ctx.nodeRes.setHeader('Server', 'Ditsmod'); + ctx.nodeRes.setHeader('Content-Type', 'application/json; charset=utf-8'); + ctx.nodeRes.end(JSON.stringify(value)); + } +} diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/postgres.service.ts b/frameworks/TypeScript/ditsmod/src/app/postgres.service.ts similarity index 100% rename from frameworks/TypeScript/ditsmod/src/app/modules/service/db/postgres.service.ts rename to frameworks/TypeScript/ditsmod/src/app/postgres.service.ts diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/tokens.ts b/frameworks/TypeScript/ditsmod/src/app/tokens.ts similarity index 100% rename from frameworks/TypeScript/ditsmod/src/app/modules/service/db/tokens.ts rename to frameworks/TypeScript/ditsmod/src/app/tokens.ts diff --git a/frameworks/TypeScript/ditsmod/src/app/modules/service/db/types.ts b/frameworks/TypeScript/ditsmod/src/app/types.ts similarity index 100% rename from frameworks/TypeScript/ditsmod/src/app/modules/service/db/types.ts rename to frameworks/TypeScript/ditsmod/src/app/types.ts diff --git a/frameworks/TypeScript/ditsmod/tsconfig.json b/frameworks/TypeScript/ditsmod/tsconfig.json index 27c048aa781..34983f83409 100644 --- a/frameworks/TypeScript/ditsmod/tsconfig.json +++ b/frameworks/TypeScript/ditsmod/tsconfig.json @@ -16,14 +16,11 @@ "noImplicitAny": true, "strictPropertyInitialization": false, "allowJs": false, - "paths": { - "#routed/*": ["./src/app/modules/routed/*"], - "#service/*": ["./src/app/modules/service/*"], - "#utils/*": ["./src/app/utils/*"], - } + "types": ["bun-types"], }, "include": [ "src", - "test" + "test", + "./spawn.ts" ] } diff --git a/frameworks/TypeScript/elysia/benchmark_config.json b/frameworks/TypeScript/elysia/benchmark_config.json index 1a0652745db..04e9073cbe8 100755 --- a/frameworks/TypeScript/elysia/benchmark_config.json +++ b/frameworks/TypeScript/elysia/benchmark_config.json @@ -62,6 +62,29 @@ "display_name": "Elysia [smol] [PostgreSQL]", "notes": "", "versus": "nodejs" + }, + "compiled": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "elysia", + "language": "TypeScript", + "flavor": "None", + "orm": "Raw", + "platform": "bun", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Elysia [Compiled]", + "notes": "", + "versus": "nodejs" } } ] diff --git a/frameworks/TypeScript/elysia/bun.lockb b/frameworks/TypeScript/elysia/bun.lockb index a5f94fd2531..5d4cdb0b97a 100755 Binary files a/frameworks/TypeScript/elysia/bun.lockb and b/frameworks/TypeScript/elysia/bun.lockb differ diff --git a/frameworks/TypeScript/elysia/elysia-compiled.dockerfile b/frameworks/TypeScript/elysia/elysia-compiled.dockerfile new file mode 100644 index 00000000000..73de65fcfa7 --- /dev/null +++ b/frameworks/TypeScript/elysia/elysia-compiled.dockerfile @@ -0,0 +1,15 @@ +FROM oven/bun:1.1 + +EXPOSE 8080 + +COPY . . + +ENV NODE_ENV production + +RUN bun install --production + +ENV DATABASE postgres + +RUN bun run compile + +CMD ["./server"] diff --git a/frameworks/TypeScript/elysia/elysia-postgres.dockerfile b/frameworks/TypeScript/elysia/elysia-postgres.dockerfile index e3fda732c79..68ce83d3325 100644 --- a/frameworks/TypeScript/elysia/elysia-postgres.dockerfile +++ b/frameworks/TypeScript/elysia/elysia-postgres.dockerfile @@ -1,4 +1,4 @@ -FROM oven/bun:1.0 +FROM oven/bun:1.1 EXPOSE 8080 @@ -8,8 +8,8 @@ ENV NODE_ENV production RUN bun install --production -RUN bun run build - ENV DATABASE postgres -CMD ["bun", "spawn.ts"] +RUN bun run build + +CMD ["bun", "./dist/index.js"] diff --git a/frameworks/TypeScript/elysia/elysia-smol-postgres.dockerfile b/frameworks/TypeScript/elysia/elysia-smol-postgres.dockerfile index c222841c81a..fa6c7a7aca7 100644 --- a/frameworks/TypeScript/elysia/elysia-smol-postgres.dockerfile +++ b/frameworks/TypeScript/elysia/elysia-smol-postgres.dockerfile @@ -1,4 +1,4 @@ -FROM oven/bun:1.0 +FROM oven/bun:1.1 EXPOSE 8080 @@ -6,12 +6,12 @@ COPY . . ENV NODE_ENV production -RUN bun install --production - -RUN bun run build +RUN bun install ENV DATABASE postgres +RUN bun run build + RUN sed -i 's/smol = false/smol = true/g' bunfig.toml -CMD ["bun", "spawn.ts"] +CMD ["bun", "./dist/index.js"] diff --git a/frameworks/TypeScript/elysia/elysia.dockerfile b/frameworks/TypeScript/elysia/elysia.dockerfile index c05d3f41878..f95b8706c6c 100644 --- a/frameworks/TypeScript/elysia/elysia.dockerfile +++ b/frameworks/TypeScript/elysia/elysia.dockerfile @@ -1,4 +1,4 @@ -FROM oven/bun:1.0 +FROM oven/bun:1.1 EXPOSE 8080 @@ -10,4 +10,4 @@ RUN bun install --production RUN bun run build -CMD ["bun", "spawn.ts"] +CMD ["bun", "./dist/index.js"] diff --git a/frameworks/TypeScript/elysia/package.json b/frameworks/TypeScript/elysia/package.json index 082042b5f55..fa95e553bc3 100644 --- a/frameworks/TypeScript/elysia/package.json +++ b/frameworks/TypeScript/elysia/package.json @@ -3,15 +3,16 @@ "version": "0.0.1", "module": "src/index.js", "devDependencies": { - "bun-types": "latest" + "typescript": "^5.5.4" }, "scripts": { "dev": "bun run --watch src/index.ts", "start": "bun run src/index.ts", - "build": "bun build --target=bun --minify src/index.ts --outdir=build" + "build": "bun build --minify --target bun --outdir dist src/index.ts", + "compile": "bun build --compile --minify --target bun --outfile server src/index.ts" }, "dependencies": { - "elysia": "^0.7.17", - "postgres": "^3.4.1" + "elysia": "^1.1.16", + "postgres": "^3.4.4" } } diff --git a/frameworks/TypeScript/elysia/spawn.ts b/frameworks/TypeScript/elysia/spawn.ts deleted file mode 100644 index ae02e7169a7..00000000000 --- a/frameworks/TypeScript/elysia/spawn.ts +++ /dev/null @@ -1,10 +0,0 @@ -import os from 'node:os'; - -// @ts-ignore -const numCPUs = os.availableParallelism(); -for (let i = 0; i < numCPUs; i++) { - Bun.spawn(['bun', 'build/index.js'], { - stdio: ['inherit', 'inherit', 'inherit'], - env: { ...process.env }, - }); -} diff --git a/frameworks/TypeScript/elysia/src/db-handlers.ts b/frameworks/TypeScript/elysia/src/db-handlers.ts index 0e099d6d8b6..b75ccfdea60 100644 --- a/frameworks/TypeScript/elysia/src/db-handlers.ts +++ b/frameworks/TypeScript/elysia/src/db-handlers.ts @@ -1,84 +1,75 @@ -import Elysia from 'elysia'; -import * as postgres from './postgres'; -import { Fortune, World } from './types'; - -const deps = new Elysia({ - name: 'deps', -}) - .decorate('db', postgres) - .decorate('generateRandomNumber', () => Math.ceil(Math.random() * 10000)) - .decorate('html', (fortunes: Fortune[]) => { - const n = fortunes.length; - - let html = ''; - for (let i = 0; i < n; i++) { - html += `${fortunes[i].id}${Bun.escapeHTML( - fortunes[i].message - )}`; - } +import { Elysia, t } from "elysia"; +import * as db from "./postgres"; +import { Fortune } from "./types"; + +function rand() { + return Math.ceil(Math.random() * 10000); +} + +function parseQueriesNumber(q?: string) { + return Math.min(parseInt(q || "1") || 1, 500); +} + +function renderTemplate(fortunes: Fortune[]) { + const n = fortunes.length; + + let html = ""; + for (let i = 0; i < n; i++) { + html += `${fortunes[i].id}${Bun.escapeHTML( + fortunes[i].message, + )}`; + } + + return `Fortunes${html}
idmessage
`; +} + +export const dbHandlers = new Elysia() + .headers({ + server: "Elysia", + }) + .get("/db", () => db.find(rand())) + .get("/fortunes", async (c) => { + const fortunes = await db.fortunes(); - return `Fortunes${html}
idmessage
`; - }); + fortunes.push({ + id: 0, + message: "Additional fortune added at request time.", + }); -const dbHandlers = new Elysia({ - name: 'db-handlers', -}) - .use(deps) - .get( - '/db', - async ({ db, generateRandomNumber }) => - await db.find(generateRandomNumber()) - ) - .get( - '/fortunes', - async ({ db, html }) => { - 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)); - - return html(fortunes); - }, - { - afterHandle({ set }) { - set.headers['content-type'] = 'text/html; charset=utf-8'; - }, - } - ) - .derive(({ query }) => ({ - numberOfObjects: Math.min(parseInt(query.queries || '1') || 1, 500), - })) - .get('/queries', async ({ db, generateRandomNumber, numberOfObjects }) => { - const worldPromises = new Array>(numberOfObjects); - - for (let i = 0; i < numberOfObjects; i++) { - worldPromises[i] = db.find(generateRandomNumber()); - } + fortunes.sort((a, b) => { + if (a.message < b.message) return -1; - const worlds = await Promise.all(worldPromises); + return 1; + }); - return worlds; + c.set.headers["content-type"] = "text/html; charset=utf-8"; + + return renderTemplate(fortunes); }) - .get('/updates', async ({ db, generateRandomNumber, numberOfObjects }) => { - const worldPromises = new Array>(numberOfObjects); + .get("/queries", (c) => { + const num = parseQueriesNumber(c.query.queries); + const worldPromises = new Array(num); - for (let i = 0; i < numberOfObjects; i++) { - worldPromises[i] = db.find(generateRandomNumber()); + for (let i = 0; i < num; i++) { + worldPromises[i] = db.find(rand()); + } + + return Promise.all(worldPromises); + }) + .get("/updates", async (c) => { + const num = parseQueriesNumber(c.query.queries); + const worldPromises = new Array(num); + + for (let i = 0; i < num; i++) { + worldPromises[i] = db.find(rand()); } const worlds = await Promise.all(worldPromises); - for (let i = 0; i < numberOfObjects; i++) { - worlds[i].randomNumber = generateRandomNumber(); + for (let i = 0; i < num; i++) { + worlds[i].randomNumber = rand(); } await db.bulkUpdate(worlds); - return worlds; }); - -export default dbHandlers; diff --git a/frameworks/TypeScript/elysia/src/index.ts b/frameworks/TypeScript/elysia/src/index.ts index dc0f0c8d20f..b34edbde4b7 100644 --- a/frameworks/TypeScript/elysia/src/index.ts +++ b/frameworks/TypeScript/elysia/src/index.ts @@ -1,25 +1,20 @@ -import { Elysia } from 'elysia'; -import dbHandlers from './db-handlers'; +import cluster from "node:cluster"; +import os from "node:os"; +import process from "node:process"; -const app = new Elysia({ - serve: { - // @ts-ignore - reusePort: true, - }, -}) - .onAfterHandle(({ set }) => { - set.headers['server'] = 'Elysia'; - }) - .decorate('HELLO_WORLD_STR', 'Hello, World!') - .get('/plaintext', ({ HELLO_WORLD_STR }) => HELLO_WORLD_STR) - .get('/json', ({ HELLO_WORLD_STR }) => ({ message: HELLO_WORLD_STR })); +if (cluster.isPrimary) { + console.log(`Primary ${process.pid} is running`); -if (process.env.DATABASE) { - app.use(dbHandlers); -} - -app.listen(8080); + const numCPUs = os.availableParallelism(); + for (let i = 0; i < numCPUs; i++) { + cluster.fork(); + } -console.info( - `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` -); + cluster.on("exit", (worker) => { + console.log(`worker ${worker.process.pid} died`); + process.exit(1); + }); +} else { + await import("./server"); + console.log(`Worker ${process.pid} started`); +} diff --git a/frameworks/TypeScript/elysia/src/postgres.ts b/frameworks/TypeScript/elysia/src/postgres.ts index ae101fc0867..cc6d4389a02 100644 --- a/frameworks/TypeScript/elysia/src/postgres.ts +++ b/frameworks/TypeScript/elysia/src/postgres.ts @@ -1,27 +1,30 @@ -import postgres from 'postgres'; -import { Fortune, World } from './types'; +import postgres from "postgres"; +import { Fortune, World } from "./types"; const sql = postgres({ - host: 'tfb-database', - user: 'benchmarkdbuser', - password: 'benchmarkdbpass', - database: 'hello_world', + host: "tfb-database", + user: "benchmarkdbuser", + password: "benchmarkdbpass", + database: "hello_world", max: 1, }); -export const fortunes = async () => - await sql`SELECT id, message FROM fortune`; +export const fortunes = () => sql`SELECT id, message FROM fortune`; -export const find = async (id: number) => - await sql`SELECT id, randomNumber FROM world WHERE id = ${id}`.then( - (arr) => arr[0] +export const find = (id: number) => + sql`SELECT id, randomNumber FROM world WHERE id = ${id}`.then( + (arr) => arr[0], ); -export const bulkUpdate = async (worlds: World[]) => - 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`; +export const bulkUpdate = (worlds: World[]) => { + worlds = worlds.toSorted((a, b) => a.id - b.id); + + const values = new Array(worlds.length); + for (let i = 0; i < worlds.length; i++) { + values[i] = [worlds[i].id, worlds[i].randomNumber]; + } + + return sql`UPDATE world SET randomNumber = (update_data.randomNumber)::int + FROM (VALUES ${sql(values)}) AS update_data (id, randomNumber) + WHERE world.id = (update_data.id)::int`; +}; diff --git a/frameworks/TypeScript/elysia/src/server.ts b/frameworks/TypeScript/elysia/src/server.ts new file mode 100644 index 00000000000..7c2c46d224d --- /dev/null +++ b/frameworks/TypeScript/elysia/src/server.ts @@ -0,0 +1,20 @@ +import { Elysia } from "elysia"; +import { dbHandlers } from "./db-handlers"; + +const app = new Elysia() + .headers({ + server: "Elysia", + }) + .get("/plaintext", "Hello, World!") + // As state on xiv in https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#requirements + // The serialization to JSON must not be cached; + // the computational effort to serialize an object to JSON must occur within the scope of handling each request. + .get("/json", () => ({ message: "Hello, World!" })) + .use((app) => { + if (Bun.env.DATABASE) app.use(dbHandlers); + + return app; + }) + .listen(8080); + +console.info(`🦊 Elysia is running at ${app.server!.url}`); diff --git a/frameworks/TypeScript/elysia/src/types.ts b/frameworks/TypeScript/elysia/src/types.ts index a40a9a0c7b2..6863ea8f4aa 100644 --- a/frameworks/TypeScript/elysia/src/types.ts +++ b/frameworks/TypeScript/elysia/src/types.ts @@ -1,9 +1,9 @@ -export type Fortune = { - id: number; - message: string; -}; +export interface Fortune { + id: number + message: string +} -export type World = { - id: number; - randomNumber: number; -}; +export interface World { + id: number + randomNumber: number +} diff --git a/frameworks/Zig/httpz/.gitignore b/frameworks/Zig/httpz/.gitignore new file mode 100644 index 00000000000..170dc0f1403 --- /dev/null +++ b/frameworks/Zig/httpz/.gitignore @@ -0,0 +1,2 @@ +zig-cache/**/*', +zig-out: 'zig-out/**/*', diff --git a/frameworks/Zig/httpz/README.md b/frameworks/Zig/httpz/README.md new file mode 100644 index 00000000000..e83169efe17 --- /dev/null +++ b/frameworks/Zig/httpz/README.md @@ -0,0 +1,25 @@ + +# [Httpz](https://github.com/karlseguin/http.zig) - An HTTP/1.1 server for Zig + +## Description + +Native Zig framework and zig http replacement + +## Test URLs + +### Test 1: JSON Encoding + + http://localhost:3000/json + +### Test 2: Plaintext + + http://localhost:3000/plaintext + +### Test 2: Single Row Query + + http://localhost:3000/db + +### Test 4: Fortunes (Template rendering) + + http://localhost:3000/fortunes + diff --git a/frameworks/Zig/httpz/benchmark_config.json b/frameworks/Zig/httpz/benchmark_config.json new file mode 100644 index 00000000000..e36c9c17a1c --- /dev/null +++ b/frameworks/Zig/httpz/benchmark_config.json @@ -0,0 +1,26 @@ +{ + "framework": "httpz", + "tests": [{ + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "db_url": "/db", + "fortune_url": "/fortunes", + "port": 3000, + "approach": "Realistic", + "classification": "Fullstack", + "database": "Postgres", + "framework": "httpz", + "language": "Zig", + "flavor": "None", + "orm": "raw", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Httpz (Zig)", + "notes": "", + "versus": "" + } + }] +} diff --git a/frameworks/Zig/httpz/build.zig b/frameworks/Zig/httpz/build.zig new file mode 100644 index 00000000000..5978de7c6aa --- /dev/null +++ b/frameworks/Zig/httpz/build.zig @@ -0,0 +1,78 @@ +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); + exe.root_module.addImport("mustache", mustache_module); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/frameworks/Zig/httpz/build.zig.zon b/frameworks/Zig/httpz/build.zig.zon new file mode 100644 index 00000000000..58b494c2fe3 --- /dev/null +++ b/frameworks/Zig/httpz/build.zig.zon @@ -0,0 +1,19 @@ +.{ .name = "Zap testing", .version = "0.1.1", .paths = .{ + "build.zig", + "build.zig.zon", + "src", +}, .dependencies = .{ + .pg = .{ .url = "https://github.com/karlseguin/pg.zig/archive/239a4468163a49d8c0d03285632eabe96003e9e2.tar.gz", .hash = "1220a1d7e51e2fa45e547c76a9e099c09d06e14b0b9bfc6baa89367f56f1ded399a0" }, + .httpz = .{ + .url = "git+https://github.com/karlseguin/http.zig?ref=zig-0.13#7d2ddae87af9b110783085c0ea6b03985faa4584", + .hash = "12208c1f2c5f730c4c03aabeb0632ade7e21914af03e6510311b449458198d0835d6", + }, + .datetimez = .{ + .url = "git+https://github.com/frmdstryr/zig-datetime#70aebf28fb3e137cd84123a9349d157a74708721", + .hash = "122077215ce36e125a490e59ec1748ffd4f6ba00d4d14f7308978e5360711d72d77f", + }, + .mustache = .{ + .url = "git+https://github.com/batiati/mustache-zig#ae5ecc1522da983dc39bb0d8b27f5d1b1d7956e3", + .hash = "1220ac9e3316ce71ad9cd66c7f215462bf5c187828b50bb3d386549bf6af004e3bb0", + }, +} } diff --git a/frameworks/Zig/httpz/httpz.dockerfile b/frameworks/Zig/httpz/httpz.dockerfile new file mode 100644 index 00000000000..5257b77ea18 --- /dev/null +++ b/frameworks/Zig/httpz/httpz.dockerfile @@ -0,0 +1,23 @@ +FROM fedora:40 + +WORKDIR /httpz + +ENV PG_USER=benchmarkdbuser +ENV PG_PASS=benchmarkdbpass +ENV PG_DB=hello_world +ENV PG_HOST=tfb-database +ENV PG_PORT=5432 + +COPY src src +COPY build.zig.zon build.zig.zon +COPY build.zig build.zig +COPY run.sh run.sh + +RUN dnf install -y zig +RUN zig version +RUN zig build -Doptimize=ReleaseFast +RUN cp /httpz/zig-out/bin/httpz /usr/local/bin + +EXPOSE 3000 + +CMD ["sh", "run.sh"] \ No newline at end of file diff --git a/frameworks/Zig/httpz/run.sh b/frameworks/Zig/httpz/run.sh new file mode 100644 index 00000000000..582c2ad0228 --- /dev/null +++ b/frameworks/Zig/httpz/run.sh @@ -0,0 +1,3 @@ +echo "Waiting for Httpz framework to start..." + +httpz \ No newline at end of file diff --git a/frameworks/Zig/httpz/src/endpoints.zig b/frameworks/Zig/httpz/src/endpoints.zig new file mode 100644 index 00000000000..0ee22b274de --- /dev/null +++ b/frameworks/Zig/httpz/src/endpoints.zig @@ -0,0 +1,192 @@ +const std = @import("std"); +const httpz = @import("httpz"); +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, + mutex: std.Thread.Mutex = .{}, +}; + +const Message = struct { + message: []const u8, +}; + +const World = struct { + id: i32, + randomNumber: i32, +}; + +const Fortune = struct { + id: i32, + message: []const u8, +}; + +pub fn plaintext(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { + try setHeaders(global.allocator, 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); + + const message = Message{ .message = "Hello, World!" }; + + try res.json(message, .{}); +} + +pub fn db(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { + try setHeaders(global.allocator, res); + + global.mutex.lock(); + const random_number = 1 + (global.prng.random().uintAtMost(u32, 9999)); + global.mutex.unlock(); + + const world = getWorld(global.pool, random_number) catch |err| { + std.debug.print("Error querying database: {}\n", .{err}); + return; + }; + + try res.json(world, .{}); +} + +pub fn fortune(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { + try setHeaders(global.allocator, res); + + const fortunes_html = try getFortunesHtml(global.allocator, global.pool); + + res.header("content-type", "text/html; charset=utf-8"); + res.body = fortunes_html; +} + +fn getWorld(pool: *pg.Pool, random_number: u32) !World{ + var conn = try pool.acquire(); + defer conn.release(); + + const row_result = try conn.row("SELECT id, randomNumber FROM World WHERE id = $1", .{random_number}); + + var row = row_result.?; + defer row.deinit() catch {}; + + return World{ .id = row.get(i32, 0), .randomNumber = row.get(i32, 1) }; +} + +fn setHeaders(allocator: Allocator, res: *httpz.Response) !void { + res.header("Server", "Httpz"); + + const now = datetimez.datetime.Date.now(); + const time = datetimez.datetime.Time.now(); + + // Wed, 17 Apr 2013 12:00:00 GMT + // Return date in ISO format YYYY-MM-DD + const TB_DATE_FMT = "{s:0>3}, {d:0>2} {s:0>3} {d:0>4} {d:0>2}:{d:0>2}:{d:0>2} GMT"; + const now_str = try std.fmt.allocPrint(allocator, TB_DATE_FMT, .{ now.weekdayName()[0..3], now.day, now.monthName()[0..3], now.year, time.hour, time.minute, time.second }); + + //defer allocator.free(now_str); + + res.header("Date", now_str); +} + +fn getFortunesHtml(allocator: Allocator, pool: *pg.Pool) ![]const u8 { + const fortunes = try getFortunes(allocator, pool); + + const raw = try mustache.allocRenderText(allocator, template,.{ .fortunes = fortunes }); + + // std.debug.print("mustache output {s}\n", .{raw}); + + const html = try deescapeHtml(allocator, raw); + + // std.debug.print("html output {s}\n", .{html}); + + return html; +} + +fn getFortunes(allocator: Allocator, pool: *pg.Pool) ![]const Fortune { + var conn = try pool.acquire(); + defer conn.release(); + + var rows = try conn.query("SELECT id, message FROM Fortune", .{}); + defer rows.deinit(); + + var fortunes = std.ArrayList(Fortune).init(allocator); + defer fortunes.deinit(); + + while (try rows.next()) |row| { + const current_fortune = Fortune{ .id = row.get(i32, 0), .message = row.get([]const u8, 1) }; + try fortunes.append(current_fortune); + } + + const zero_fortune = Fortune{ .id = 0, .message = "Additional fortune added at request time." }; + try fortunes.append(zero_fortune); + + const fortunes_slice = try fortunes.toOwnedSlice(); + std.mem.sort(Fortune, fortunes_slice, {}, cmpFortuneByMessage); + + return fortunes_slice; +} + +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 { + var output = std.ArrayList(u8).init(allocator); + defer output.deinit(); + + var i: usize = 0; + while (i < input.len) { + if (std.mem.startsWith(u8, input[i..], " ")) { + try output.append(' '); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], """)) { + try output.append('"'); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], "&")) { + try output.append('&'); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], "'")) { + try output.append('\''); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], "(")) { + try output.append('('); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], ")")) { + try output.append(')'); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], "+")) { + try output.append('+'); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], ",")) { + try output.append(','); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], ".")) { + try output.append('.'); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], "/")) { + try output.append('/'); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], ":")) { + try output.append(':'); + i += 5; + } else if (std.mem.startsWith(u8, input[i..], ";")) { + try output.append(';'); + i += 5; + } else { + try output.append(input[i]); + i += 1; + } + } + + return output.toOwnedSlice(); +} + diff --git a/frameworks/Zig/httpz/src/main.zig b/frameworks/Zig/httpz/src/main.zig new file mode 100644 index 00000000000..ae2c1a70ac4 --- /dev/null +++ b/frameworks/Zig/httpz/src/main.zig @@ -0,0 +1,71 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const httpz = @import("httpz"); +const pg = @import("pg"); +const datetimez = @import("datetimez"); +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; + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{ + .thread_safe = true, + }){}; + + const allocator = gpa.allocator(); + + var pg_pool = try pool.initPool(allocator); + defer pg_pool.deinit(); + + var prng = std.rand.DefaultPrng.init(@as(u64, @bitCast(std.time.milliTimestamp()))); + + var global = endpoints.Global{ .pool = pg_pool, .prng = &prng, .allocator = allocator }; + + server = try httpz.ServerApp(*endpoints.Global).init(allocator, .{ + .port = 3000, .address = "0.0.0.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}, + .mask = std.posix.empty_sigset, + .flags = 0, + }, null); + + var router = server.router(); + router.get("/json", endpoints.json); + router.get("/plaintext", endpoints.plaintext); + router.get("/db", endpoints.db); + router.get("/fortunes", endpoints.fortune); + + std.debug.print("Httpz listening at 0.0.0.0:{d}\n", .{3000}); + + 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 diff --git a/frameworks/Zig/httpz/src/pool.zig b/frameworks/Zig/httpz/src/pool.zig new file mode 100644 index 00000000000..c41cb329540 --- /dev/null +++ b/frameworks/Zig/httpz/src/pool.zig @@ -0,0 +1,87 @@ +const std = @import("std"); +const regex = @import("regex"); +const pg = @import("pg"); + +const Allocator = std.mem.Allocator; +const Pool = pg.Pool; +const ArrayList = std.ArrayList; + +pub fn initPool(allocator: Allocator) !*pg.Pool { + const info = try parsePostgresConnStr(allocator); + //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, + .connect = .{ + .port = info.port, + .host = info.hostname, + }, + .auth = .{ + .username = info.username, + .database = info.database, + .password = info.password, + }, + .timeout = 10_000, + }); + + return pg_pool; +} + +pub const ConnectionInfo = struct { + username: []const u8, + password: []const u8, + hostname: []const u8, + port: u16, + database: []const u8, +}; + +fn addressAsString(address: std.net.Address) ![]const u8 { + const bytes = @as(*const [4]u8, @ptrCast(&address.in.sa.addr)); + + var buffer: [256]u8 = undefined; + var source = std.io.StreamSource{ .buffer = std.io.fixedBufferStream(&buffer) }; + var writer = source.writer(); + + //try writer.writeAll("Hello, World!"); + + try writer.print("{}.{}.{}.{}", .{ + bytes[0], + bytes[1], + bytes[2], + bytes[3], + }); + + const output = source.buffer.getWritten(); + + return output; +} + +fn parsePostgresConnStr(allocator: Allocator) !ConnectionInfo { + const pg_port = try getEnvVar(allocator, "PG_PORT", "5432"); + // std.debug.print("tfb port {s}\n", .{pg_port}); + var port = try std.fmt.parseInt(u16, pg_port, 0); + + if (port == 0) { + port = 5432; + } + + return ConnectionInfo{ + .username = try getEnvVar(allocator, "PG_USER", "benchmarkdbuser"), + .password = try getEnvVar(allocator, "PG_PASS", "benchmarkdbpass"), + .hostname = try getEnvVar(allocator, "PG_HOST", "localhost"), + .port = port, + .database = try getEnvVar(allocator, "PG_DB", "hello_world"), + }; +} + +fn getEnvVar(allocator: Allocator, name: []const u8, default: []const u8) ![]const u8 { + const env_var = std.process.getEnvVarOwned(allocator, name) catch |err| switch (err) { + error.EnvironmentVariableNotFound => return default, + error.OutOfMemory => return err, + error.InvalidWtf8 => return err, + }; + + if (env_var.len == 0) return default; + + return env_var; +} diff --git a/frameworks/Zig/zap/build-nginx-conf.sh b/frameworks/Zig/zap/build-nginx-conf.sh new file mode 100644 index 00000000000..ecb55c80bfa --- /dev/null +++ b/frameworks/Zig/zap/build-nginx-conf.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +CPU_COUNT=$(nproc) +P=3000 +END=$(($P+$CPU_COUNT)) +CONF="" + +while [ $P -lt $END ]; do + CONF+="\t\tserver 127.0.0.1:$P;\n" + let P=P+1 +done + +sed -i "s|# replace|$CONF|g" nginx.conf diff --git a/frameworks/Zig/zap/build.zig b/frameworks/Zig/zap/build.zig index 70c740351a1..2da55a07981 100644 --- a/frameworks/Zig/zap/build.zig +++ b/frameworks/Zig/zap/build.zig @@ -1,9 +1,12 @@ 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 { +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 @@ -15,41 +18,35 @@ pub fn build(b: *std.Build) void { // 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 = "zap", // 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 = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); - //exe.addPackagePath("random", "src/random.zig"); - const zap = b.dependency("zap", .{ .target = target, .optimize = optimize, .openssl = false, // set to true to enable TLS support }); - exe.addModule("zap", zap.module("zap")); - const pg = b.dependency("pg", .{ - .target = target, - .optimize = optimize, - }); - exe.addModule("pg", pg.module("pg")); + var modules = ModuleMap.init(allocator); + defer modules.deinit(); - const dig = b.dependency("dig", .{ - .target = target, - .optimize = optimize, - }); - exe.addModule("dns", dig.module("dns")); + const zap_module = b.dependency("zap", dep_opts).module("zap"); + const pg_module = b.dependency("pg", dep_opts).module("pg"); + + try modules.put("zap", zap_module); + try modules.put("pg", pg_module); - // const mustache = b.dependency("mustache", .{ - // .target = target, - // .optimize = optimize, - // }); - // exe.addModule("mustache", mustache.module("mustache")); + // // Expose this as a module that others can import + exe.root_module.addImport("zap", zap_module); + exe.root_module.addImport("pg", pg_module); exe.linkLibrary(zap.artifact("facil.io")); @@ -80,20 +77,4 @@ pub fn build(b: *std.Build) void { // This will evaluate the `run` step rather than the default, which is "install". const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); - - // Creates a step for unit testing. This only builds the test executable - // but does not run it. - const unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, - .target = target, - .optimize = optimize, - }); - - const run_unit_tests = b.addRunArtifact(unit_tests); - - // Similar to creating the run step earlier, this exposes a `test` step to - // the `zig build --help` menu, providing a way for the user to request - // running the unit tests. - const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_unit_tests.step); } diff --git a/frameworks/Zig/zap/build.zig.zon b/frameworks/Zig/zap/build.zig.zon index 3abfe719e55..3f492b7d954 100644 --- a/frameworks/Zig/zap/build.zig.zon +++ b/frameworks/Zig/zap/build.zig.zon @@ -1,21 +1,12 @@ -.{ - .name = "Zap testing", - .version = "0.1.0", - - .dependencies = .{ - // zap v0.5.1 - .zap = .{ - .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.5.1.tar.gz", - .hash = "1220d4802fb09d4e99c0e7265f90d6f3cfdc3e5e31c1b05f0924ee2dd26d9d6dbbf4", - }, - .pg = .{ - .url = "https://github.com/karlseguin/pg.zig/archive/f3f4a0b3b9996bfb1bf9bd0bdd0d73b36e915a86.tar.gz", - .hash = "1220337202642ee66408a35f254549f22cf3a096c6fa6c28e6f87a0161d5a6c0f4ab" - }, - .dig = .{ - .url = "https://github.com/lun-4/zigdig/archive/2ec407ec3c7f347e747717977958e9ba339eb82f.tar.gz", - .hash = "1220dfdb3089dfe9a4e4bc1226fcff08d91d0c0853f287d98d8b81270da251790331" - }, - - } -} \ No newline at end of file +.{ .name = "Zap testing", .version = "0.1.1", .paths = .{ + "build.zig", + "build.zig.zon", + "src", +}, .dependencies = .{ + .zap = .{ + .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.8.0.tar.gz", + .hash = "12209936c3333b53b53edcf453b1670babb9ae8c2197b1ca627c01e72670e20c1a21", + }, + .pg = .{ .url = "https://github.com/karlseguin/pg.zig/archive/239a4468163a49d8c0d03285632eabe96003e9e2.tar.gz", + .hash = "1220a1d7e51e2fa45e547c76a9e099c09d06e14b0b9bfc6baa89367f56f1ded399a0" }, +} } diff --git a/frameworks/Zig/zap/nginx.conf b/frameworks/Zig/zap/nginx.conf new file mode 100644 index 00000000000..f394b2ad206 --- /dev/null +++ b/frameworks/Zig/zap/nginx.conf @@ -0,0 +1,33 @@ +error_log stderr; +worker_processes auto; + +events { + worker_connections 65535; + multi_accept off; +} + +http { + default_type application/octet-stream; + client_body_temp_path /tmp; + access_log off; + + sendfile on; + tcp_nopush on; + keepalive_requests 100000; + keepalive_timeout 65; + + upstream workers { + # replace + } + + server { + listen 8080; + server_name tfb-server; + + location / { + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://workers; + } + } +} diff --git a/frameworks/Zig/zap/run.sh b/frameworks/Zig/zap/run.sh deleted file mode 100644 index 95c2266e8bd..00000000000 --- a/frameworks/Zig/zap/run.sh +++ /dev/null @@ -1,3 +0,0 @@ -echo "Waiting for ZAP to start..." - -zap \ No newline at end of file diff --git a/frameworks/Zig/zap/src/endpoints.zig b/frameworks/Zig/zap/src/endpoints.zig index aeeffbf4e0a..44a3afc43e2 100644 --- a/frameworks/Zig/zap/src/endpoints.zig +++ b/frameworks/Zig/zap/src/endpoints.zig @@ -64,27 +64,27 @@ pub const FortunesEndpoint = struct { defer conn.release(); var rows = try conn.query("SELECT id, message FROM Fortune", .{}); - rows.deinit(); + defer rows.deinit(); var fortunes = std.ArrayList(Fortune).init(middleware.SharedAllocator.getAllocator()); defer fortunes.deinit(); while (try rows.next()) |row| { - var fortune = Fortune{ .id = row.get(i32, 0), .message = row.get([]const u8, 1) }; - _ = try fortunes.append(fortune); + const fortune = Fortune{ .id = row.get(i32, 0), .message = row.get([]const u8, 1) }; + try fortunes.append(fortune); } - var fortune = Fortune{ .id = 0, .message = "Additional fortune added at request time." }; - _ = try fortunes.append(fortune); + const fortune = Fortune{ .id = 0, .message = "Additional fortune added at request time." }; + try fortunes.append(fortune); - var fortunes_slice = try fortunes.toOwnedSlice(); + const fortunes_slice = try fortunes.toOwnedSlice(); std.mem.sort(Fortune, fortunes_slice, {}, cmpFortuneByMessage); return fortunes_slice; } fn getFortunesHtml(self: *Self, pool: *pg.Pool) ![]const u8 { - var fortunes = try getFortunes(pool); + const fortunes = try getFortunes(pool); self.mutex.lock(); const ret = self.mustache.build(.{ .fortunes = fortunes }); @@ -95,7 +95,7 @@ pub const FortunesEndpoint = struct { // std.debug.print("mustache output {s}\n", .{raw}); - var html = try deescapeHtml(raw); + const html = try deescapeHtml(raw); // std.debug.print("html output {s}\n", .{html}); @@ -103,7 +103,7 @@ pub const FortunesEndpoint = struct { } pub fn get(ep: *zap.Endpoint, req: zap.Request) void { - const self = @fieldParentPtr(Self, "ep", ep); + const self: *FortunesEndpoint = @fieldParentPtr("ep", ep); if (!checkPath(ep, req)) return; @@ -118,7 +118,7 @@ pub const FortunesEndpoint = struct { } } - var fortunes_html = getFortunesHtml(self, pool) catch return; + const fortunes_html = getFortunesHtml(self, pool) catch return; req.sendBody(fortunes_html) catch return; @@ -146,7 +146,7 @@ pub const DbEndpoint = struct { } pub fn get(ep: *zap.Endpoint, req: zap.Request) void { - const self = @fieldParentPtr(Self, "ep", ep); + const self: *DbEndpoint = @fieldParentPtr("ep", ep); if (!checkPath(ep, req)) return; @@ -168,23 +168,30 @@ pub const DbEndpoint = struct { } } - // std.debug.print("Attempting to return random: {}\n", .{random_number}); - if (random_number == 0) { return; } - var conn = pool.acquire() catch return; - defer conn.release(); - - var row_result = conn.row("SELECT id, randomNumber FROM World WHERE id = $1", .{random_number}) catch |err| { + const json_to_send = getJson(pool, random_number) catch |err| { std.debug.print("Error querying database: {}\n", .{err}); return; }; + + req.sendBody(json_to_send) catch return; + + return; + } + + fn getJson(pool: *pg.Pool, random_number: u32) ![]const u8{ + var conn = try pool.acquire(); + defer conn.release(); + + const row_result = try conn.row("SELECT id, randomNumber FROM World WHERE id = $1", .{random_number}); + var row = row_result.?; - defer row.deinit(); + defer row.deinit() catch {}; - var world = World{ .id = row.get(i32, 0), .randomNumber = row.get(i32, 1) }; + const world = World{ .id = row.get(i32, 0), .randomNumber = row.get(i32, 1) }; var buf: [100]u8 = undefined; var json_to_send: []const u8 = undefined; @@ -194,9 +201,7 @@ pub const DbEndpoint = struct { json_to_send = "null"; } - req.sendBody(json_to_send) catch return; - - return; + return json_to_send; } }; @@ -218,7 +223,7 @@ pub const PlaintextEndpoint = struct { } pub fn get(ep: *zap.Endpoint, req: zap.Request) void { - const self = @fieldParentPtr(Self, "ep", ep); + const self: *PlaintextEndpoint = @fieldParentPtr("ep", ep); _ = self; if (!checkPath(ep, req)) return; @@ -248,14 +253,14 @@ pub const JsonEndpoint = struct { } pub fn get(ep: *zap.Endpoint, req: zap.Request) void { - const self = @fieldParentPtr(Self, "ep", ep); + const self: *JsonEndpoint = @fieldParentPtr("ep", ep); _ = self; if (!checkPath(ep, req)) return; req.setContentType(.JSON) catch return; - var message = Message{ .message = "Hello, World!" }; + const message = Message{ .message = "Hello, World!" }; var buf: [100]u8 = undefined; var json_to_send: []const u8 = undefined; diff --git a/frameworks/Zig/zap/src/main.zig b/frameworks/Zig/zap/src/main.zig index 489fb5d5b7e..e2792726f04 100644 --- a/frameworks/Zig/zap/src/main.zig +++ b/frameworks/Zig/zap/src/main.zig @@ -1,8 +1,8 @@ const std = @import("std"); +const builtin = @import("builtin"); const zap = @import("zap"); const pg = @import("pg"); const regex = @import("regex"); -const dns = @import("dns"); const pool = @import("pool.zig"); const endpoints = @import("endpoints.zig"); @@ -21,16 +21,30 @@ pub fn main() !void { .child_allocator = gpa.allocator(), }; - var allocator = tsa.allocator(); + const allocator = tsa.allocator(); + + var zap_port: []u8 = undefined; + var arg_string = try std.fmt.allocPrint(allocator, "{s}", .{"0"}); + defer allocator.free(arg_string); + + var args = try std.process.argsWithAllocator(allocator); + defer args.deinit(); + while (args.next()) |arg| { + arg_string = try std.fmt.allocPrint(allocator, "{s}", .{arg}); + + zap_port = arg_string; // use arg + } + + var port = try std.fmt.parseInt(u16, zap_port, 0); + + if (port == 0) { + port = 3000; + } var pg_pool = try pool.initPool(allocator); defer pg_pool.deinit(); - var rnd = std.rand.DefaultPrng.init(blk: { - var seed: u64 = undefined; - try std.os.getrandom(std.mem.asBytes(&seed)); - break :blk seed; - }); + var prng = std.rand.DefaultPrng.init(@as(u64, @bitCast(std.time.milliTimestamp()))); middleware.SharedAllocator.init(allocator); @@ -66,13 +80,13 @@ pub fn main() !void { ); var headerHandler = middleware.HeaderMiddleWare.init(dbEndpointHandler.getHandler()); - var prngHandler = middleware.PrngMiddleWare.init(headerHandler.getHandler(), &rnd); + var prngHandler = middleware.RandomMiddleWare.init(headerHandler.getHandler(), &prng); var pgHandler = middleware.PgMiddleWare.init(prngHandler.getHandler(), pg_pool); var listener = try zap.Middleware.Listener(middleware.Context).init( .{ .on_request = null, // must be null - .port = 3000, + .port = port, .log = false, .max_clients = 100000, }, @@ -82,14 +96,15 @@ pub fn main() !void { ); try listener.listen(); - const cpuCount = @as(i16, @intCast(std.Thread.getCpuCount() catch 1)); + //const cpuCount = @as(i16, @intCast(std.Thread.getCpuCount() catch 1)); + //const workers = if (builtin.mode == .Debug) 1 else cpuCount; + const threads = 128; - std.debug.print("Listening on 0.0.0.0:3000 on {d} threads\n", .{cpuCount}); + std.debug.print("Listening at 0.0.0.0:{d} on {d} threads\n", .{port, threads}); // start worker threads zap.start(.{ - .threads = 16 * cpuCount, + .threads = threads, .workers = 1, }); } - diff --git a/frameworks/Zig/zap/src/middleware.zig b/frameworks/Zig/zap/src/middleware.zig index 20c01b7ffde..99fb9255c0c 100644 --- a/frameworks/Zig/zap/src/middleware.zig +++ b/frameworks/Zig/zap/src/middleware.zig @@ -22,7 +22,7 @@ pub const SharedAllocator = struct { // create a combined context struct pub const Context = struct { - prng: ?PrngMiddleWare.Prng = null, + prng: ?RandomMiddleWare.Prng = null, pg: ?PgMiddleWare.Pg = null, }; @@ -47,7 +47,7 @@ pub const HeaderMiddleWare = struct { // note that the first parameter is of type *Handler, not *Self !!! pub fn onRequest(handler: *Handler, req: zap.Request, context: *Context) bool { // this is how we would get our self pointer - var self = @fieldParentPtr(Self, "handler", handler); + const self: *Self = @fieldParentPtr("handler", handler); _ = self; req.setHeader("Server", "Zap") catch return false; @@ -57,7 +57,7 @@ pub const HeaderMiddleWare = struct { } }; -pub const PrngMiddleWare = struct { +pub const RandomMiddleWare = struct { handler: Handler, rnd: *std.rand.DefaultPrng, @@ -83,7 +83,7 @@ pub const PrngMiddleWare = struct { pub fn onRequest(handler: *Handler, req: zap.Request, context: *Context) bool { // this is how we would get our self pointer - var self = @fieldParentPtr(Self, "handler", handler); + const self: *RandomMiddleWare = @fieldParentPtr("handler", handler); context.prng = Prng{ .rnd = self.rnd }; @@ -118,7 +118,7 @@ pub const PgMiddleWare = struct { pub fn onRequest(handler: *Handler, req: zap.Request, context: *Context) bool { // this is how we would get our self pointer - var self = @fieldParentPtr(Self, "handler", handler); + const self: *Self = @fieldParentPtr("handler", handler); // do our work: fill in the user field of the context context.pg = Pg{ .pool = self.pool }; diff --git a/frameworks/Zig/zap/src/pool.zig b/frameworks/Zig/zap/src/pool.zig index 7241780a464..6615ae217ce 100644 --- a/frameworks/Zig/zap/src/pool.zig +++ b/frameworks/Zig/zap/src/pool.zig @@ -1,7 +1,7 @@ const std = @import("std"); -const pg = @import("pg"); const regex = @import("regex"); -const dns = @import("dns"); +const dns = @import("dig"); +const pg = @import("pg"); const Allocator = std.mem.Allocator; const Pool = pg.Pool; @@ -9,31 +9,22 @@ const ArrayList = std.ArrayList; const Regex = regex.Regex; pub fn initPool(allocator: Allocator) !*pg.Pool { - const info = try parsePostgresConnStr(); - std.debug.print("Cconnection info: {s}:{s}@{s}:{d}/{s}\n", .{ info.username, info.password, info.hostname, info.port, info.database }); - - const hostname = std.os.getenv("PG_HOST") orelse "localhost"; - var addresses = try dns.helpers.getAddressList(hostname, allocator); - defer addresses.deinit(); - - var hostAddress = std.net.Address.parseIp("127.0.0.1", 0) catch unreachable; - - for (addresses.addrs) |address| { - hostAddress = address; - } - - std.debug.print("tfb hostname {}\n", .{hostAddress.in}); - - const host = try addressAsString(hostAddress); - - var pg_pool = try Pool.init(allocator, .{ .size = 28, .connect = .{ - .port = info.port, - .host = host, - }, .auth = .{ - .username = info.username, - .database = info.database, - .password = info.password, - }, .timeout = 10_000,}); + const info = try parsePostgresConnStr(allocator); + //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, + .connect = .{ + .port = info.port, + .host = info.hostname, + }, + .auth = .{ + .username = info.username, + .database = info.database, + .password = info.password, + }, + .timeout = 10_000, + }); return pg_pool; } @@ -67,12 +58,32 @@ fn addressAsString(address: std.net.Address) ![]const u8 { return output; } -fn parsePostgresConnStr() !ConnectionInfo { +fn parsePostgresConnStr(allocator: Allocator) !ConnectionInfo { + const pg_port = try getEnvVar(allocator, "PG_PORT", "5432"); + // std.debug.print("tfb port {s}\n", .{pg_port}); + var port = try std.fmt.parseInt(u16, pg_port, 0); + + if (port == 0) { + port = 5432; + } + return ConnectionInfo{ - .username = std.os.getenv("PG_USER") orelse "benchmarkdbuser", - .password = std.os.getenv("PG_PASS") orelse "benchmarkdbpass", - .hostname = std.os.getenv("PG_HOST") orelse "localhost", // , - .port = try std.fmt.parseInt(u16, std.os.getenv("PG_PORT") orelse "5432", 0), - .database = std.os.getenv("PG_DB") orelse "hello_world", + .username = try getEnvVar(allocator, "PG_USER", "benchmarkdbuser"), + .password = try getEnvVar(allocator, "PG_PASS", "benchmarkdbpass"), + .hostname = try getEnvVar(allocator, "PG_HOST", "localhost"), + .port = port, + .database = try getEnvVar(allocator, "PG_DB", "hello_world"), }; } + +fn getEnvVar(allocator: Allocator, name: []const u8, default: []const u8) ![]const u8 { + const env_var = std.process.getEnvVarOwned(allocator, name) catch |err| switch (err) { + error.EnvironmentVariableNotFound => return default, + error.OutOfMemory => return err, + error.InvalidWtf8 => return err, + }; + + if (env_var.len == 0) return default; + + return env_var; +} diff --git a/frameworks/Zig/zap/start-servers.sh b/frameworks/Zig/zap/start-servers.sh new file mode 100644 index 00000000000..b5cf175de41 --- /dev/null +++ b/frameworks/Zig/zap/start-servers.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +CPU_COUNT=$(nproc) +P=3000 +END=$(($P+$CPU_COUNT)) + +while [ $P -lt $END ]; do + zap $P & + let P=P+1 +done diff --git a/frameworks/Zig/zap/zap.dockerfile b/frameworks/Zig/zap/zap.dockerfile index 580f037bcef..71123f4f3e7 100644 --- a/frameworks/Zig/zap/zap.dockerfile +++ b/frameworks/Zig/zap/zap.dockerfile @@ -1,27 +1,4 @@ -#FROM ziglang/static-base:llvm15-aarch64-3 as build -FROM buddyspencer/ziglang:0.11.0-r3 as build - -WORKDIR /zap - -COPY src src - -COPY build.zig.zon build.zig.zon -COPY build.zig build.zig - -RUN apk update -RUN apk add yaml-dev sqlite-dev -RUN apk add bind-tools -RUN apk add --no-cache bash -RUN dig +short localhost | head -n 1 -RUN zig build -Doptimize=ReleaseFast --prefix-exe-dir /usr/bin -RUN zig version -RUN ls - -EXPOSE 3000 - -CMD ["sh", "run.sh"] - -FROM alpine:3.19 +FROM fedora:40 AS build WORKDIR /zap @@ -31,12 +8,23 @@ ENV PG_DB=hello_world ENV PG_HOST=tfb-database ENV PG_PORT=5432 -COPY run.sh run.sh +COPY src src +COPY build.zig.zon build.zig.zon +COPY build.zig build.zig +COPY start-servers.sh start-servers.sh +COPY build-nginx-conf.sh build-nginx-conf.sh +COPY nginx.conf nginx.conf + +RUN chmod +x start-servers.sh +RUN chmod +x build-nginx-conf.sh -RUN apk update +RUN ./build-nginx-conf.sh -COPY --from=build /usr/bin/zap /usr/bin/zap +RUN dnf install -y zig nginx +RUN zig version +RUN zig build -Doptimize=ReleaseFast +RUN cp /zap/zig-out/bin/zap /usr/local/bin -EXPOSE 3000 +EXPOSE 8080 -CMD ["sh", "run.sh"] \ No newline at end of file +CMD ./start-servers.sh && nginx -c /zap/nginx.conf -g "daemon off;" \ No newline at end of file diff --git a/frameworks/Zig/zinc/.gitignore b/frameworks/Zig/zinc/.gitignore new file mode 100644 index 00000000000..170dc0f1403 --- /dev/null +++ b/frameworks/Zig/zinc/.gitignore @@ -0,0 +1,2 @@ +zig-cache/**/*', +zig-out: 'zig-out/**/*', diff --git a/frameworks/Zig/zinc/README.md b/frameworks/Zig/zinc/README.md new file mode 100755 index 00000000000..acf0f93bbcb --- /dev/null +++ b/frameworks/Zig/zinc/README.md @@ -0,0 +1,32 @@ +# [Zinc](https://zinc.zon.dev) web framework + +## Description + +Zinc is a web framework written in pure Zig with a focus on high performance, usability, security, and extensibility. + +* [Documentation](https://zinc.zon.dev/) + +### Some features are: +- **Fast** +- **Custom allocator** +- **Multithreading** +- **Middleware** +- **Routes grouping** +- **Rendering built-in** +- **Extensible** +- **Suite of unit tests** +- **Usability** + +## Important Libraries +The tests were run with: +* [Software](https://zinc.zon.dev/) +* [Example](https://github.com/zon-dev/zinc-examples) + +## Test URLs +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext diff --git a/frameworks/Zig/zinc/benchmark_config.json b/frameworks/Zig/zinc/benchmark_config.json new file mode 100755 index 00000000000..2e2e874ba2d --- /dev/null +++ b/frameworks/Zig/zinc/benchmark_config.json @@ -0,0 +1,26 @@ +{ + "framework": "zinc", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 3000, + "approach": "Realistic", + "classification": "Fullstack", + "database": "None", + "framework": "Zinc", + "language": "Zig", + "flavor": "None", + "orm": "None", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Zinc", + "notes": "", + "versus": "None" + } + } + ] +} diff --git a/frameworks/Zig/zinc/build.zig b/frameworks/Zig/zinc/build.zig new file mode 100644 index 00000000000..e5be63940e5 --- /dev/null +++ b/frameworks/Zig/zinc/build.zig @@ -0,0 +1,78 @@ +const std = @import("std"); + +// 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 not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "zinc", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const zinc = b.dependency("zinc", .{ + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("zinc", zinc.module("zinc")); + + const datetime = b.dependency("zig-datetime", .{ + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("datetime", datetime.module("zig-datetime")); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/frameworks/Zig/zinc/build.zig.zon b/frameworks/Zig/zinc/build.zig.zon new file mode 100644 index 00000000000..9a4c8f302cf --- /dev/null +++ b/frameworks/Zig/zinc/build.zig.zon @@ -0,0 +1,27 @@ +.{ + .name = "zinc", + .version = "0.1.0", + .dependencies = .{ + .zinc = .{ + .url = "https://github.com/zon-dev/zinc/archive/refs/tags/0.1.0-beta.5.tar.gz", + .hash = "12201444aa36b4a83f262f319e7c17ccdcff9fbde2efbeb5fc94f1a07eda0d99428e", + }, + .@"zig-datetime" = .{ + .url = "git+https://github.com/frmdstryr/zig-datetime#70aebf28fb3e137cd84123a9349d157a74708721", + .hash = "122077215ce36e125a490e59ec1748ffd4f6ba00d4d14f7308978e5360711d72d77f", + }, + .pg = .{ + .url = "git+https://github.com/karlseguin/pg.zig#21db2306aff657802f9cb10a1e7f8fe9c33e7990", + .hash = "1220df8995ceea78a4a37a505fc779ded75725d0606c33fded26103953524dde1619", + }, + .mustache = .{ + .url = "git+https://github.com/batiati/mustache-zig#ac358646ab9e6123285b90c947ecd40f7966d531", + .hash = "1220cd6e1b49bdd0a568682957dab9a6864554755908f7de990ec7c050f58cf41da2", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/frameworks/Zig/zinc/run.sh b/frameworks/Zig/zinc/run.sh new file mode 100644 index 00000000000..639c542fc3e --- /dev/null +++ b/frameworks/Zig/zinc/run.sh @@ -0,0 +1,3 @@ +echo "Waiting for Zinc framework to start..." + +zinc \ No newline at end of file diff --git a/frameworks/Zig/zinc/src/main.zig b/frameworks/Zig/zinc/src/main.zig new file mode 100644 index 00000000000..f06104dc4f1 --- /dev/null +++ b/frameworks/Zig/zinc/src/main.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const zinc = @import("zinc"); +const Datetime = @import("datetime").datetime.Datetime; + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{ .thread_safe = true }){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var z = try zinc.init(.{ + .port = 3000, + .allocator = allocator, + .num_threads = 16 * @as(u8, @intCast(std.Thread.getCpuCount() catch 1)), + }); + defer z.deinit(); + + var router = z.getRouter(); + try router.use(&.{setupHeader}); + try router.get("/json", json); + try router.get("/plaintext", plaintext); + + try z.run(); +} + +fn plaintext(ctx: *zinc.Context) anyerror!void { + try ctx.setHeader("Content-Type", "text/plain; charset=utf-8"); + try ctx.setBody("Hello, world!"); +} + +fn json(ctx: *zinc.Context) anyerror!void { + try ctx.json(.{ .message = "Hello, World!" }, .{}); +} + +fn setupHeader(ctx: *zinc.Context) anyerror!void { + try ctx.setHeader("Server", "Zinc"); + + const now = Datetime.now(); + const now_str = try now.formatHttp(ctx.allocator); + // defer ctx.allocator.free(now_str); + + // The time is now: Fri, 20 Dec 2019 22:03:02 UTC + try ctx.setHeader("date", now_str); +} diff --git a/frameworks/Zig/zinc/zinc.dockerfile b/frameworks/Zig/zinc/zinc.dockerfile new file mode 100644 index 00000000000..11b64881d2c --- /dev/null +++ b/frameworks/Zig/zinc/zinc.dockerfile @@ -0,0 +1,21 @@ +FROM fedora:40 AS build + +WORKDIR /zinc + +COPY src src +COPY run.sh run.sh + +COPY build.zig.zon build.zig.zon +COPY build.zig build.zig + +RUN dnf install -y zig +RUN zig version +RUN zig build -Doptimize=ReleaseFast +RUN cp /zinc/zig-out/bin/zinc /usr/local/bin + +EXPOSE 3000 +ARG BENCHMARK_ENV +ARG TFB_TEST_DATABASE +ARG TFB_TEST_NAME + +CMD ["sh", "run.sh"] diff --git a/toolset/benchmark/benchmarker.py b/toolset/benchmark/benchmarker.py index 84e6be29912..8b5ce49d65b 100644 --- a/toolset/benchmark/benchmarker.py +++ b/toolset/benchmark/benchmarker.py @@ -1,3 +1,6 @@ +import threading + +from docker.models.containers import Container from toolset.utils.output_helper import log, FNULL from toolset.utils.docker_helper import DockerHelper from toolset.utils.time_logger import TimeLogger @@ -263,12 +266,6 @@ def benchmark_type(test_type): log("BENCHMARKING %s ... " % test_type.upper(), file=benchmark_log) test = framework_test.runTests[test_type] - raw_file = self.results.get_raw_file(framework_test.name, - test_type) - if not os.path.exists(raw_file): - # Open to create the empty file - with open(raw_file, 'w'): - pass if not test.failed: # Begin resource usage metrics collection @@ -281,8 +278,8 @@ def benchmark_type(test_type): framework_test.port, test.get_url())) - self.docker_helper.benchmark(script, script_variables, - raw_file) + benchmark_container = self.docker_helper.benchmark(script, script_variables) + self.__log_container_output(benchmark_container, framework_test, test_type) # End resource usage metrics collection self.__end_logging() @@ -323,3 +320,31 @@ def __end_logging(self): self.subprocess_handle.terminate() self.subprocess_handle.communicate() + def __log_container_output(self, container: Container, framework_test, test_type) -> None: + def save_docker_logs(stream): + raw_file_path = self.results.get_raw_file(framework_test.name, test_type) + with open(raw_file_path, 'w') as file: + for line in stream: + log(line.decode(), file=file) + + def save_docker_stats(stream): + docker_file_path = self.results.get_docker_stats_file(framework_test.name, test_type) + with open(docker_file_path, 'w') as file: + file.write('[\n') + is_first_line = True + for line in stream: + if is_first_line: + is_first_line = False + else: + file.write(',') + file.write(line.decode()) + file.write(']') + + threads = [ + threading.Thread(target=lambda: save_docker_logs(container.logs(stream=True))), + threading.Thread(target=lambda: save_docker_stats(container.stats(stream=True))) + ] + + [thread.start() for thread in threads] + [thread.join() for thread in threads] + diff --git a/toolset/databases/mysql/create.sql b/toolset/databases/mysql/create.sql index 94cb995deb3..d97fb37f490 100644 --- a/toolset/databases/mysql/create.sql +++ b/toolset/databases/mysql/create.sql @@ -2,14 +2,14 @@ # http://stackoverflow.com/questions/37719818/the-server-time-zone-value-aest-is-unrecognized-or-represents-more-than-one-ti SET GLOBAL time_zone = '+00:00'; -CREATE USER IF NOT EXISTS 'benchmarkdbuser'@'%' IDENTIFIED WITH mysql_native_password BY 'benchmarkdbpass'; -CREATE USER IF NOT EXISTS 'benchmarkdbuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'benchmarkdbpass'; +CREATE USER IF NOT EXISTS 'benchmarkdbuser'@'%' IDENTIFIED WITH caching_sha2_password BY 'benchmarkdbpass'; +CREATE USER IF NOT EXISTS 'benchmarkdbuser'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'benchmarkdbpass'; -- GitHub Actions/CI run the database server on the same system as the benchmarks. -- Because we setup MySQL with the skip-name-resolve option, the IP address 127.0.0.1 might not be resolved to localhost -- anymore. This does not seem to matter, as long as Unix sockets are being used (e.g. when setting up the docker image), -- because the host is set to be localhost implicitly, but it matters for local TCP connections. -CREATE USER IF NOT EXISTS 'benchmarkdbuser'@'127.0.0.1' IDENTIFIED WITH mysql_native_password BY 'benchmarkdbpass'; +CREATE USER IF NOT EXISTS 'benchmarkdbuser'@'127.0.0.1' IDENTIFIED WITH caching_sha2_password BY 'benchmarkdbpass'; # modified from SO answer http://stackoverflow.com/questions/5125096/for-loop-in-mysql CREATE DATABASE IF NOT EXISTS hello_world; diff --git a/toolset/databases/mysql/my.cnf b/toolset/databases/mysql/my.cnf index 40ffa8dd9eb..e1a401ae2d9 100644 --- a/toolset/databases/mysql/my.cnf +++ b/toolset/databases/mysql/my.cnf @@ -16,7 +16,7 @@ default-character-set=utf8 # * Basic Settings # default-storage-engine = innodb -mysql_native_password = ON +#mysql_native_password = ON # disabled in v9 #default_authentication_plugin = mysql_native_password user = mysql diff --git a/toolset/databases/mysql/mysql.dockerfile b/toolset/databases/mysql/mysql.dockerfile index 1aae5eb7d38..b5c5b6a67bb 100644 --- a/toolset/databases/mysql/mysql.dockerfile +++ b/toolset/databases/mysql/mysql.dockerfile @@ -1,4 +1,4 @@ -FROM mysql:8.4 +FROM mysql:9.0 ENV MYSQL_ROOT_PASSWORD=root ENV MYSQL_USER=benchmarkdbuser diff --git a/toolset/databases/postgres/postgres.dockerfile b/toolset/databases/postgres/postgres.dockerfile index 757d3ba79e8..aa2bcebf01d 100644 --- a/toolset/databases/postgres/postgres.dockerfile +++ b/toolset/databases/postgres/postgres.dockerfile @@ -1,4 +1,4 @@ -FROM postgres:16-bookworm +FROM postgres:17-bookworm ENV PGDATA=/ssd/postgresql \ POSTGRES_DB=hello_world \ diff --git a/toolset/utils/docker_helper.py b/toolset/utils/docker_helper.py index 1f3ea692ee1..e48a910e99d 100644 --- a/toolset/utils/docker_helper.py +++ b/toolset/utils/docker_helper.py @@ -420,16 +420,11 @@ def server_container_exists(self, container_id_or_name): except: return False - def benchmark(self, script, variables, raw_file): + def benchmark(self, script, variables): ''' Runs the given remote_script on the wrk container on the client machine. ''' - def watch_container(container): - with open(raw_file, 'w') as benchmark_file: - for line in container.logs(stream=True): - log(line.decode(), file=benchmark_file) - if self.benchmarker.config.network_mode is None: sysctl = {'net.core.somaxconn': 65535} else: @@ -438,8 +433,7 @@ def watch_container(container): ulimit = [{'name': 'nofile', 'hard': 65535, 'soft': 65535}] - watch_container( - self.client.containers.run( + return self.client.containers.run( "techempower/tfb.wrk", "/bin/bash /%s" % script, environment=variables, @@ -450,4 +444,4 @@ def watch_container(container): ulimits=ulimit, sysctls=sysctl, remove=True, - log_config={'type': None})) + log_config={'type': None}) diff --git a/toolset/utils/results.py b/toolset/utils/results.py index 7b745a90bc0..0df26636404 100644 --- a/toolset/utils/results.py +++ b/toolset/utils/results.py @@ -212,29 +212,34 @@ def load(self): except (ValueError, IOError): pass + def __make_dir_for_file(self, test_name: str, test_type: str, file_name: str): + path = os.path.join(self.directory, test_name, test_type, file_name) + try: + os.makedirs(os.path.dirname(path), exist_ok=True) + except OSError: + pass + return path + + def get_docker_stats_file(self, test_name, test_type): + ''' + Returns the stats file name for this test_name and + Example: fw_root/results/timestamp/test_type/test_name/stats.txt + ''' + return self.__make_dir_for_file(test_name, test_type, "docker_stats.json") + def get_raw_file(self, test_name, test_type): ''' Returns the output file for this test_name and test_type Example: fw_root/results/timestamp/test_type/test_name/raw.txt ''' - path = os.path.join(self.directory, test_name, test_type, "raw.txt") - try: - os.makedirs(os.path.dirname(path)) - except OSError: - pass - return path + return self.__make_dir_for_file(test_name, test_type, "raw.txt") def get_stats_file(self, test_name, test_type): ''' Returns the stats file name for this test_name and Example: fw_root/results/timestamp/test_type/test_name/stats.txt ''' - path = os.path.join(self.directory, test_name, test_type, "stats.txt") - try: - os.makedirs(os.path.dirname(path)) - except OSError: - pass - return path + return self.__make_dir_for_file(test_name, test_type, "stats.txt") def report_verify_results(self, framework_test, test_type, result): '''