feat: click controller performance improvements

- return tuple directly from the block
- avoid to parse remote_address in the main thread
- avoid to replace headers, add only
- avoid separate variables for one-single use
This commit is contained in:
sjdonado
2025-03-20 20:43:15 +01:00
parent 4ae6ef39d5
commit e67ed7165b
3 changed files with 61 additions and 40 deletions
+2 -2
View File
@@ -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 **30MiB** and single CPU core consumption around 20%.
Lightweight URL shortener API service with minimal resource requirements. Average memory consumption is under **30MiB** and single CPU core consumption around 30%.
Performance: Avg **1K reqs/sec**, latency 33ms, [benchmark](docs/SETUP.md#benchmark).
Performance: Avg **1K reqs/sec**, latency 30ms, [benchmark](docs/SETUP.md#benchmark).
Self-hosted with [Dokku](docs/SETUP.md#dokku) and [Docker Compose](docs/SETUP.md#docker-compose).
+9 -19
View File
@@ -11,21 +11,19 @@ module App::Controllers
def self.redirect_handler
->(env : HTTP::Server::Context) {
slug = env.params.url["slug"]
link_id, url = Database.raw_query("SELECT id, url FROM links WHERE slug = (?) LIMIT 1", env.params.url["slug"]) do |result|
result.move_next ? {result.read(Int64), result.read(String)} : nil
end || raise App::NotFoundException.new(env)
link_data = nil
Database.raw_query("SELECT id, url FROM links WHERE slug = (?) LIMIT 1", slug) do |result|
if result.move_next
link_data = {result.read(Int64), result.read(String)}
end
end
raise App::NotFoundException.new(env) unless link_data
remote_address = env.request.headers["Cf-Connecting-Ip"]? || env.request.remote_address.to_s
link_id, url = link_data
client_ip = IpLookup.ip_from_address(env.request.headers["Cf-Connecting-Ip"]? || env.request.remote_address.to_s)
env.response.status_code = 301
env.response.headers.add("Location", url)
env.response.headers.add("X-Forwarded-For", remote_address)
spawn do
begin
client_ip = IpLookup.ip_from_address(remote_address)
user_agent_str = env.request.headers["User-Agent"]?
referer = env.request.headers["Referer"]?.try { |r| URI.parse(r).host rescue r } || env.params.query["utm_source"]? || "Direct"
@@ -34,7 +32,7 @@ module App::Controllers
click = App::Models::Click.new
click.link_id = link_id
click.country = client_ip ? IpLookup.new(client_ip).try(&.country.try(&.code)) : nil
click.user_agent = ua_parser.to_s
click.user_agent = user_agent_str
click.browser = ua_parser.try(&.family)
click.os = ua_parser.try(&.os.try(&.family))
click.referer = referer
@@ -44,14 +42,6 @@ module App::Controllers
Log.error { "Click tracking error: #{ex.message}" }
end
end
env.response.status_code = 301
env.response.headers.add("Location", url)
env.response.headers.add("X-Forwarded-For", client_ip.to_s)
env.response.print ""
env.response.close
return
}
end
end
+50 -19
View File
@@ -126,33 +126,64 @@ 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/slug2202
Selected link for benchmarking: http://localhost:4000/slug6268
Starting benchmark with Bombardier...
Bombarding http://localhost:4000/slug2202 for 59s using 30 connection(s)
Bombarding http://localhost:4000/slug6268 for 59s using 30 connection(s)
[==============================================================================================] 59s
Done!
Statistics Avg Stdev Max
Reqs/sec 1321.38 427.84 2067.24
Latency 33.19ms 51.13ms 2.00s
Reqs/sec 1113.53 384.47 1642.40
Latency 27.13ms 6.87ms 246.27ms
Latency Distribution
50% 30.01ms
75% 34.38ms
90% 40.59ms
95% 48.35ms
99% 65.21ms
50% 25.13ms
75% 27.60ms
90% 32.34ms
95% 36.06ms
99% 50.99ms
HTTP codes:
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
1xx - 0, 2xx - 0, 3xx - 65268, 4xx - 0, 5xx - 0
others - 0
Throughput: 308.80KB/s
Benchmark completed successfully.
Analyzing resource usage...
**** Resource Usage Statistics ****
Measurements: 21
Average CPU Usage: 22.53%
Average Memory Usage: 28.62 MiB
Peak CPU Usage: 35.21%
Peak Memory Usage: 62.14 MiB
Measurements: 31
Average CPU Usage: 30.49%
Average Memory Usage: 64.82 MiB
Peak CPU Usage: 34.12%
Peak Memory Usage: 65.45 MiB
Cleanup completed. Resource usage data saved in resource_usage.txt
~/p/bit> cat resource_usage.txt
Timestamp CPU(%) Memory(MiB)
1742499555 0.0 61.76
1742499557 0.01 61.76
1742499559 26.73 65.13
1742499561 32.59 65.16
1742499563 32.66 65.42
1742499565 33.32 65.45
1742499567 31.84 65.2
1742499569 33.01 65.4
1742499571 32.56 65.23
1742499573 32.86 65.23
1742499575 33.31 65.24
1742499577 33.0 65.06
1742499579 32.98 65.07
1742499581 33.42 64.93
1742499583 32.98 64.91
1742499585 32.85 64.93
1742499587 33.39 64.94
1742499589 32.88 64.95
1742499591 31.9 64.95
1742499593 34.12 65.21
1742499595 32.85 64.94
1742499597 32.95 64.89
1742499599 33.88 64.9
1742499601 31.93 64.89
1742499603 33.67 64.89
1742499605 32.62 64.89
1742499607 31.12 65.01
1742499609 31.04 64.77
1742499611 33.95 64.77
1742499613 32.3 64.68
1742499615 32.52 64.94
```