Compare commits
No commits in common. "master" and "feat-integration-tests" have entirely different histories.
master
...
feat-integ
692
Cargo.lock
generated
14
Cargo.toml
|
@ -18,10 +18,10 @@ serde = { version = "1", features = ["derive"] }
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
|
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.4.0"
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
derive_builder = "0.20.0"
|
derive_builder = "0.11.2"
|
||||||
config = { version = "0.14", features = ["toml"] }
|
config = { version = "0.11", features = ["toml"] }
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
url = { version = "2.2.2", features = ["serde"]}
|
url = { version = "2.2.2", features = ["serde"]}
|
||||||
async-trait = "0.1.36"
|
async-trait = "0.1.36"
|
||||||
|
@ -29,22 +29,22 @@ clap = { version = "4.1.11", features = ["derive", "env"] }
|
||||||
tokio = { version = "1.0", default-features = false, features = ["sync", "macros", "rt-multi-thread", "time"] }
|
tokio = { version = "1.0", default-features = false, features = ["sync", "macros", "rt-multi-thread", "time"] }
|
||||||
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
|
||||||
actix = "0.13.0"
|
actix = "0.13.0"
|
||||||
tonic = { version = "0.11.0", features = ["transport", "channel"] }
|
tonic = { version = "0.10.2", features = ["transport", "channel"] }
|
||||||
prost = "0.12.3"
|
prost = "0.12.3"
|
||||||
tokio-stream = "0.1.14"
|
tokio-stream = "0.1.14"
|
||||||
async-stream = "0.3.5"
|
async-stream = "0.3.5"
|
||||||
actix-rt = "2.9.0"
|
actix-rt = "2.9.0"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
tower-service = "0.3.2"
|
tower-service = "0.3.2"
|
||||||
dashmap = { version = "6.0.0", features = ["serde"] }
|
dashmap = { version = "5.5.3", features = ["serde"] }
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
tonic-build = "0.11.0"
|
tonic-build = "0.10.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
base64 = "0.22.0"
|
base64 = "0.13.0"
|
||||||
anyhow = "1.0.63"
|
anyhow = "1.0.63"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
blinker==1.8.2
|
blinker==1.7.0
|
||||||
Brotli==1.1.0
|
Brotli==1.1.0
|
||||||
certifi==2024.8.30
|
certifi==2023.11.17
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.3.2
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
ConfigArgParse==1.7
|
ConfigArgParse==1.7
|
||||||
Flask==3.0.3
|
Flask==3.0.0
|
||||||
Flask-BasicAuth==0.2.0
|
Flask-BasicAuth==0.2.0
|
||||||
Flask-Cors==5.0.0
|
Flask-Cors==4.0.0
|
||||||
gevent==24.2.1
|
gevent==23.9.1
|
||||||
geventhttpclient==2.3.1
|
geventhttpclient==2.0.11
|
||||||
greenlet==3.1.1
|
greenlet==3.0.2
|
||||||
idna==3.10
|
idna==3.6
|
||||||
itsdangerous==2.2.0
|
itsdangerous==2.1.2
|
||||||
Jinja2==3.1.4
|
Jinja2==3.1.2
|
||||||
locust==2.31.6
|
locust==2.20.0
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==2.1.3
|
||||||
msgpack==1.1.0
|
msgpack==1.0.7
|
||||||
psutil==6.0.0
|
psutil==5.9.7
|
||||||
pyzmq==26.2.0
|
pyzmq==25.1.2
|
||||||
requests==2.32.3
|
requests==2.31.0
|
||||||
roundrobin==0.0.4
|
roundrobin==0.0.4
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
urllib3==2.2.3
|
urllib3==2.1.0
|
||||||
Werkzeug==3.0.4
|
Werkzeug==3.0.1
|
||||||
zope.event==5.0
|
zope.event==5.0
|
||||||
zope.interface==7.0.3
|
zope.interface==6.1
|
||||||
|
|
|
@ -1,212 +0,0 @@
|
||||||
# Benchmark Report
|
|
||||||
|
|
||||||
Benchmarks were run at various stages of development to keep track of
|
|
||||||
performance. Tech stacks were changed and the implementation optimized
|
|
||||||
to increase throughput. This report summarizes the findings of the
|
|
||||||
benchmarks
|
|
||||||
|
|
||||||
Ultimately, we were able to identify a bottleneck that was previously
|
|
||||||
hidden in mCaptcha (hidden because a different bottleneck like DB access
|
|
||||||
eclipsed it :p) [and were able to increase performance of the critical
|
|
||||||
path by ~147 times](https://git.batsense.net/mCaptcha/dcache/pulls/3)
|
|
||||||
through a trivial optimization.
|
|
||||||
|
|
||||||
## Environment
|
|
||||||
|
|
||||||
These benchmarks were run on a noisy development laptop and should be
|
|
||||||
used for guidance only.
|
|
||||||
|
|
||||||
- CPU: AMD Ryzen 5 5600U with Radeon Graphics (12) @ 4.289GHz
|
|
||||||
- Memory: 22849MiB
|
|
||||||
- OS: Arch Linux x86_64
|
|
||||||
- Kernel: 6.6.7-arch1-1
|
|
||||||
- rustc: 1.73.0 (cc66ad468 2023-10-03)
|
|
||||||
|
|
||||||
## Baseline: Tech stack version 1
|
|
||||||
|
|
||||||
Actix Web based networking with JSON for message format. Was chosen for
|
|
||||||
prototyping, and was later used to set a baseline.
|
|
||||||
|
|
||||||
## Without connection pooling in server-to-server communications
|
|
||||||
|
|
||||||
### Single requests (no batching)
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
|
|
||||||
<summary>Peak throughput observed was 1117 request/second (please click
|
|
||||||
to see charts)</summary>
|
|
||||||
|
|
||||||
|
|
||||||
#### Total number of requests vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Response times(ms) vs time
|
|
||||||
|
|
||||||
_1703969194.png>)
|
|
||||||
|
|
||||||
#### Number of concurrent users vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Batched requests
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>
|
|
||||||
Each network request contained 1,000 application requests, so peak throughput observed was 1,800 request/second.
|
|
||||||
Please click to see charts</summary>
|
|
||||||
|
|
||||||
|
|
||||||
#### Total number of requests vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Response times(ms) vs time
|
|
||||||
|
|
||||||
_1703968582.png>))
|
|
||||||
|
|
||||||
#### Number of concurrent users vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## With connection pooling in server-to-server communications
|
|
||||||
|
|
||||||
|
|
||||||
### Single requests (no batching)
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>
|
|
||||||
Peak throughput observed was 3904 request/second. Please click to see
|
|
||||||
charts</summary>
|
|
||||||
|
|
||||||
|
|
||||||
#### Total number of requests vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Response times(ms) vs time
|
|
||||||
|
|
||||||
_1703968215.png>)
|
|
||||||
|
|
||||||
#### Number of concurrent users vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Batched requests
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>
|
|
||||||
Each network request contained 1,000 application requests, so peak throughput observed was 15,800 request/second.
|
|
||||||
Please click to see charts.
|
|
||||||
</summary>
|
|
||||||
|
|
||||||
|
|
||||||
#### Total number of requests vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Response times(ms) vs time
|
|
||||||
|
|
||||||
_1703968582.png>))
|
|
||||||
|
|
||||||
#### Number of concurrent users vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
## Tech stack version 2
|
|
||||||
|
|
||||||
Tonic for the network stack and GRPC for wire format. We ran over a
|
|
||||||
dozen benchmarks with this tech stack. The trend was similar to the ones
|
|
||||||
observed above: throughput was higher when connection pool was used and
|
|
||||||
even higher when requests were batched. _But_ the throughput of all of these benchmarks were lower than the
|
|
||||||
baseline benchmarks!
|
|
||||||
|
|
||||||
The CPU was busier. We put it through
|
|
||||||
[flamgragh](https://github.com/flamegraph-rs/flamegraph) and hit it with
|
|
||||||
the same test suite to identify compute-heavy areas. The result was
|
|
||||||
unexpected:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
libmCaptcha's [AddVisitor
|
|
||||||
handler](https://github.com/mCaptcha/libmcaptcha/blob/e3f456f35b2c9e55e0475b01b3e05d48b21fd51f/src/master/embedded/counter.rs#L124)
|
|
||||||
was taking up 59% of CPU time of the entire test run. This is a very
|
|
||||||
critical part of the variable difficulty factor PoW algorithm that
|
|
||||||
mCaptcha uses. We never ran into this bottleneck before because in other
|
|
||||||
cache implementations, it was always preceded with a database request.
|
|
||||||
It surfaced here as we are using in-memory data sources in dcache.
|
|
||||||
|
|
||||||
libmCaptcha uses an actor-based approach with message passing for clean
|
|
||||||
concurrent state management. Message passing is generally faster in most
|
|
||||||
cases, but in our case, sharing memory using CPU's concurrent primitives
|
|
||||||
turned out to be significantly faster:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
CPU time was reduced from 59% to 0.4%, roughly by one 147 times!
|
|
||||||
|
|
||||||
With this fix in place:
|
|
||||||
|
|
||||||
|
|
||||||
### Connection pooled server-to-server communications, single requests (no batching)
|
|
||||||
|
|
||||||
Peak throughput observed was 4816 request/second, ~1000 requests/second
|
|
||||||
more than baseline.
|
|
||||||
|
|
||||||
|
|
||||||
#### Total number of requests vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Response times(ms) vs time
|
|
||||||
|
|
||||||
_1703970940.png)
|
|
||||||
|
|
||||||
#### Number of concurrent users vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### Connection pooled server-to-server communications, batched requests
|
|
||||||
|
|
||||||
|
|
||||||
Each network request contained 1,000 application requests, so peak throughput observed was 95,700 request/second. This six times higher than baseline.
|
|
||||||
Please click to see charts.
|
|
||||||
|
|
||||||
|
|
||||||
#### Total number of requests vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Response times(ms) vs time
|
|
||||||
|
|
||||||
_1703971082.png)
|
|
||||||
|
|
||||||
#### Number of concurrent users vs time
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details>
|
|
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 28 KiB |
|
@ -10,7 +10,7 @@
|
||||||
<script src="https://cdn.jsdelivr.net/npm/britecharts@3/dist/bundled/britecharts.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/britecharts@3/dist/bundled/britecharts.min.js"></script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/britecharts@3/dist/css/britecharts.min.css" type="text/css" /></head>
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/britecharts@3/dist/css/britecharts.min.css" type="text/css" /></head>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/1.0.3/css/bulma.min.css" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css" />
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"config:recommended",
|
|
||||||
":dependencyDashboard"
|
|
||||||
],
|
|
||||||
"labels": [
|
|
||||||
"renovate-bot"
|
|
||||||
],
|
|
||||||
"prHourlyLimit": 0,
|
|
||||||
"timezone": "Asia/kolkata",
|
|
||||||
"prCreation": "immediate",
|
|
||||||
"vulnerabilityAlerts": {
|
|
||||||
"enabled": true,
|
|
||||||
"labels": [
|
|
||||||
"renovate-bot",
|
|
||||||
"renovate-security",
|
|
||||||
"security"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +1,32 @@
|
||||||
asyncio==3.4.3
|
asyncio==3.4.3
|
||||||
blinker==1.8.2
|
blinker==1.7.0
|
||||||
Brotli==1.1.0
|
Brotli==1.1.0
|
||||||
certifi==2024.8.30
|
certifi==2023.11.17
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.3.2
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
ConfigArgParse==1.7
|
ConfigArgParse==1.7
|
||||||
Flask==3.0.3
|
Flask==3.0.0
|
||||||
Flask-BasicAuth==0.2.0
|
Flask-BasicAuth==0.2.0
|
||||||
Flask-Cors==5.0.0
|
Flask-Cors==4.0.0
|
||||||
gevent==24.2.1
|
gevent==23.9.1
|
||||||
geventhttpclient==2.3.1
|
geventhttpclient==2.0.11
|
||||||
greenlet==3.1.1
|
greenlet==3.0.2
|
||||||
grpc-interceptor==0.15.4
|
grpc-interceptor==0.15.4
|
||||||
grpcio==1.66.1
|
grpcio==1.60.0
|
||||||
grpcio-tools==1.60.0
|
grpcio-tools==1.60.0
|
||||||
idna==3.10
|
idna==3.6
|
||||||
itsdangerous==2.2.0
|
itsdangerous==2.1.2
|
||||||
Jinja2==3.1.4
|
Jinja2==3.1.2
|
||||||
locust==2.31.6
|
locust==2.20.0
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==2.1.3
|
||||||
msgpack==1.1.0
|
msgpack==1.0.7
|
||||||
protobuf==4.25.5
|
protobuf==4.25.1
|
||||||
psutil==6.0.0
|
psutil==5.9.7
|
||||||
pyzmq==26.2.0
|
pyzmq==25.1.2
|
||||||
requests==2.32.3
|
requests==2.31.0
|
||||||
roundrobin==0.0.4
|
roundrobin==0.0.4
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
urllib3==2.2.3
|
urllib3==2.1.0
|
||||||
Werkzeug==3.0.4
|
Werkzeug==3.0.1
|
||||||
zope.event==5.0
|
zope.event==5.0
|
||||||
zope.interface==7.0.3
|
zope.interface==6.1
|
||||||
|
|