From f2b63c00a3b790463d299f0e57a8554b270e48b8 Mon Sep 17 00:00:00 2001 From: sjdonado Date: Thu, 20 Mar 2025 13:03:15 +0100 Subject: [PATCH] chore: run benchmark --- README.md | 4 ++-- app/controllers/click.cr | 2 -- app/lib/database.cr | 4 +--- benchmark.cr | 4 ++-- db/seed.sql | 46 +++++++++++++++------------------------- docs/SETUP.md | 44 ++++++++++++++++++++------------------ 6 files changed, 45 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 32da956..9d5b1d4 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![Docker Stars](https://img.shields.io/docker/stars/sjdonado/bit.svg)](https://hub.docker.com/r/sjdonado/bit) [![Docker Image Size](https://img.shields.io/docker/image-size/sjdonado/bit/latest)](https://hub.docker.com/r/sjdonado/bit) -Lightweight URL shortener API service with minimal resource requirements. Average memory consumption is under **40MiB** and single CPU core consumption under 40%. +Lightweight URL shortener API service with minimal resource requirements. Average memory consumption is under **30MiB** and single CPU core consumption around 20%. -Performance: Avg **1K reqs/sec**, latency 8ms (100K requests using 100 connections, [benchmark](docs/SETUP.md#benchmark)). +Performance: Avg **1K reqs/sec**, latency 33ms, [benchmark](docs/SETUP.md#benchmark). Self-hosted with [Dokku](docs/SETUP.md#dokku) and [Docker Compose](docs/SETUP.md#docker-compose). diff --git a/app/controllers/click.cr b/app/controllers/click.cr index 4bd8b05..7ef8241 100644 --- a/app/controllers/click.cr +++ b/app/controllers/click.cr @@ -30,8 +30,6 @@ module App::Controllers @env.response.headers["X-Forwarded-For"] = client_ip.to_s @env.response.headers["Connection"] = "close" - @env.response.flush - spawn do begin user_agent_str = @env.request.headers["User-Agent"]? diff --git a/app/lib/database.cr b/app/lib/database.cr index 1a0ddd5..d484a33 100644 --- a/app/lib/database.cr +++ b/app/lib/database.cr @@ -15,9 +15,7 @@ module App::Lib db_url = base_url + separator + "&journal_mode=WAL" + "&synchronous=NORMAL" + # Better performance with reasonable safety - "&foreign_keys=true" + - "&cache_size=10000" + # Larger cache (10MB) for frequently accessed data - "&wal_autocheckpoint=10000" # Less frequent checkpoints + "&foreign_keys=true" conf.uri = db_url end diff --git a/benchmark.cr b/benchmark.cr index d8d8307..312e15f 100755 --- a/benchmark.cr +++ b/benchmark.cr @@ -8,7 +8,7 @@ require "file_utils" SERVER_URL = "http://localhost:4000" API_URL = "#{SERVER_URL}/api/links" API_KEY = "secure_api_key_1" -TIME = "60s" +TIME = "59s" RESOURCE_USAGE_INTERVAL = 1 CONTAINER_NAME = "bit" @@ -159,7 +159,7 @@ def run_benchmark sleep 2.seconds process = Process.new( "bombardier", - ["-d", TIME.to_s, "-l", "--fasthttp", random_link], + ["-d", TIME.to_s, "-c", "30", "-l", "--fasthttp", random_link], output: Process::Redirect::Inherit, error: Process::Redirect::Inherit ) diff --git a/db/seed.sql b/db/seed.sql index 9e05ff3..3202e6c 100644 --- a/db/seed.sql +++ b/db/seed.sql @@ -3,12 +3,12 @@ VALUES ('User 1', 'secure_api_key_1'), ('User 2', 'secure_api_key_2'); --- Create 20,000 links (10,000 per user) +-- Create 10,000 links (5,000 per user) WITH RECURSIVE link_numbers(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM link_numbers - LIMIT 20000 + LIMIT 10000 ) INSERT INTO links (user_id, slug, url) SELECT @@ -17,44 +17,35 @@ SELECT 'https://sjdonado.com/page/' || n FROM link_numbers; --- Create 1,000 clicks per link (20,000,000 total) --- Using batched approach for better performance -CREATE TEMP TABLE link_ids AS SELECT id FROM links; - -CREATE TEMP TABLE click_counts(link_id, count) AS +-- Create 1,000 clicks per link (10 million total) WITH RECURSIVE counts(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM counts LIMIT 1000 ) -SELECT l.id, c.n -FROM link_ids l -CROSS JOIN counts c; - --- Insert clicks from the count table INSERT INTO clicks (link_id, user_agent, browser, os, referer, country) SELECT - link_id, - CASE (count % 5) + l.id, + CASE (c.n % 5) WHEN 0 THEN 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' WHEN 1 THEN 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15)' WHEN 2 THEN 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0)' WHEN 3 THEN 'Mozilla/5.0 (X11; Linux x86_64)' ELSE 'Mozilla/5.0 (Android 11; Mobile)' END, - CASE (count % 3) - WHEN 0 THEN 'Chrome' - WHEN 1 THEN 'Firefox' + CASE (c.n % 3) + WHEN 0 THEN 'Firefox' + WHEN 1 THEN 'Chrome' ELSE 'Safari' END, - CASE (count % 4) - WHEN 0 THEN 'Windows' - WHEN 1 THEN 'macOS' + CASE (c.n % 4) + WHEN 0 THEN 'macOS' + WHEN 1 THEN 'Windows' WHEN 2 THEN 'iOS' ELSE 'Android' END, - CASE (count % 6) + CASE (c.n % 6) WHEN 0 THEN 'https://sjdonado.com' WHEN 1 THEN 'https://donado.co' WHEN 2 THEN 'https://idonthavespotify.donado.co' @@ -62,9 +53,9 @@ SELECT WHEN 4 THEN 'https://github.com/sjdonado' ELSE NULL END, - CASE (count % 10) - WHEN 0 THEN 'US' - WHEN 1 THEN 'UK' + CASE (c.n % 10) + WHEN 0 THEN 'Colombia' + WHEN 1 THEN 'Brazil' WHEN 2 THEN 'Canada' WHEN 3 THEN 'Germany' WHEN 4 THEN 'France' @@ -74,8 +65,5 @@ SELECT WHEN 8 THEN 'India' ELSE 'China' END -FROM click_counts; - --- Clean up -DROP TABLE link_ids; -DROP TABLE click_counts; +FROM links l +CROSS JOIN counts c; diff --git a/docs/SETUP.md b/docs/SETUP.md index 594de0f..e80ad12 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -109,14 +109,8 @@ ENV=test crystal spec ## Benchmark -RAM: 1GiB -CPU: Apple M3 Pro - -**Summary** -- Reqs/sec (average central tendency): (1328.47 + 1357.19 + 1407.03) / 3 + (1785.03 + 1789.06 + 1778.84) / 3 = 3148.54 reqs/sec -- Latency (average central tendency): (76.19 + 74.68 + 71.85) / 3 - (29.43 + 14.50 + 9.42) / 3 = 56.4ms -- Latency (for conservative capacity planning): (76.19 + 74.68 + 71.85) / 3 + (29.43 + 14.50 + 9.42) / 3 = 89.63ms -- Best single run: 18041.44 reqs/sec +- Colima: cpu 1, mem 1 +- SoC: Apple M3 Pro ``` ~/p/bit> colima start --cpu 1 --memory 1 @@ -132,25 +126,33 @@ Waiting for the application to be ready... Seeding the database... Checking seed results... Fetching all created links from /api/links... -Selected link for benchmarking: http://localhost:4000/slug187082 +Selected link for benchmarking: http://localhost:4000/slug2202 Starting benchmark with Bombardier... -Bombarding http://localhost:4000/slug187082 with 100000 request(s) using 100 connection(s) - 100000 / 100000 [==============================================================] 100.00% 12180/s 8s +Bombarding http://localhost:4000/slug2202 for 59s using 30 connection(s) +[==============================================================================================] 59s Done! Statistics Avg Stdev Max - Reqs/sec 12335.45 3288.95 20393.16 - Latency 8.11ms 1.89ms 35.42ms + Reqs/sec 1321.38 427.84 2067.24 + Latency 33.19ms 51.13ms 2.00s + Latency Distribution + 50% 30.01ms + 75% 34.38ms + 90% 40.59ms + 95% 48.35ms + 99% 65.21ms HTTP codes: - 1xx - 0, 2xx - 0, 3xx - 0, 4xx - 0, 5xx - 100000 - others - 0 - Throughput: 2.93MB/s + 1xx - 0, 2xx - 0, 3xx - 39618, 4xx - 0, 5xx - 0 + others - 13712 + Errors: + dial tcp [::1]:4000: connect: connection refused - 13712 + Throughput: 180.24KB/s Benchmark completed successfully. Analyzing resource usage... **** Resource Usage Statistics **** - Measurements: 5 - Average CPU Usage: 39.5% - Average Memory Usage: 35.25 MiB - Peak CPU Usage: 82.25% - Peak Memory Usage: 37.45 MiB + Measurements: 21 + Average CPU Usage: 22.53% + Average Memory Usage: 28.62 MiB + Peak CPU Usage: 35.21% + Peak Memory Usage: 62.14 MiB Cleanup completed. Resource usage data saved in resource_usage.txt ```