Skip to content

Commit 2785af1

Browse files
authored
Merge pull request #95
Migrate from Rocket to Axum
2 parents c95e956 + aed12b3 commit 2785af1

File tree

169 files changed

+3883
-3963
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

169 files changed

+3883
-3963
lines changed

.zed/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"Shell Script": {
88
"hard_tabs": true,
99
"format_on_save": "on"
10+
},
11+
"Markdown": {
12+
"wrap_guides": [96]
1013
}
1114
},
1215
"language_overrides": {
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# ADR: Use Axum instead of Rocket
2+
3+
- Date: **2025-01-01**
4+
- Author: **Rémi Bardon <[[email protected]](mailto:[email protected])>**
5+
<!-- Proposed|Accepted|Rejected, with date and channel if applicable -->
6+
- Status: **Accepted** via [#95](https://github.com/prose-im/prose-pod-api/pull/95) (2025-01-01)
7+
<!-- "ø" or a nested unordered list linking to other ADRs and their date -->
8+
- Relates to: ø
9+
<!-- "ø" or a nested unordered list linking to other ADRs and their date -->
10+
- Superseded by: ø
11+
<!-- "No" or "Yes" with the deprecation date -->
12+
- Deprecated: No
13+
14+
## Context
15+
16+
<!--
17+
This section describes the forces at play, including technological, political,
18+
social, and project local. These forces are probably in tension, and should be
19+
called out as such. The language in this section is value-neutral. It is simply
20+
describing facts.
21+
-->
22+
23+
Up until today, we have been using [Rocket] to power the Prose Pod API.
24+
However, it had multiple drawbacks:
25+
26+
- Its lack of middlewares (see [`tower`’s documentation]) made authorization
27+
laborious and error-prone (which is something we really don’t want on that topic).
28+
- Because of how errors are managed in [request guards], we had to create a [`LazyGuard`]
29+
(see the request guard code for more explanations). Consequently, we’d often have about
30+
30% of boilerplate in route definitions, just to “work around” a limitation of [Rocket].
31+
See [`check_dns_records_route`], which was 4 LLOCs[^lloc] long while it could have been
32+
a one-liner, or [`init_first_account_route`], which had 50% of boilerplate LLOCs[^lloc].
33+
- [Observability][observability] would have been very complex to implement, if not impossible
34+
to apply on all routes automatically. In addition, [Rocket]’s logging wasn’t very useful
35+
and silently prevented tracing logs from being printed for a long time (until [`2267262`]).
36+
- Its usage of macros would sometimes prevent auto-completion or context-sensitive
37+
navigation in IDEs, which would impact developer productivity.
38+
- [Rocket] uses a `Rocket.toml` config file, which people would have had to create
39+
and maintain in addition to the `Prose.toml` config file.
40+
It would be a lot better to have all the configuration we support in `Prose.toml`.
41+
- Release builds stayed broken for a long period because of a `#[cfg(debug_assertions)]`
42+
annotation on a route function parameter. To fix it, we had to duplicate the whole route,
43+
making more room for mistakes.
44+
- Also, see [“Rocket is dead. (?)” on r/rust] about Rocket having a bus factor of 1[^needs].
45+
46+
For this reason, we searched for alternatives which would fit our needs better[^needs].
47+
The most notable Rust HTTP server libraries we found (apart from [Rocket]) are:
48+
49+
- [Axum]
50+
- Developed by the [Tokio] team (see [“Announcing Axum” on the Tokio Blog]),
51+
which means it’s performant, well integrated and will be maintained.
52+
- Fastest full-stack web application framework (all programming languages considered),
53+
[according to TechEmpower][techempower-r22].
54+
- Takes full advantage of the [`tower`] and [`tower-http`] ecosystem of middlewares,
55+
services, and utilities. Also enables sharing middlewares with applications written
56+
using [`hyper`] or [`tonic`].
57+
- [Actix Web]
58+
- Is unmaintained (see [“A sad day for Rust”] by its original author).
59+
It was brought back to life just after the announcement (see [`actix-web#1289`]),
60+
but it has been slowing down since then (see [the Contributors Insight]).
61+
- [`warp`]
62+
- Seems a bit immature, and we found better alternatives.
63+
- [`hyper`]
64+
- > hyper is a relatively low-level library, meant to be a building block for libraries
65+
> and applications.
66+
- We need a higher-level library.
67+
68+
## Decision
69+
70+
<!--
71+
This section describes our response to these forces. It is stated in full
72+
sentences, with active voice. "We will …"
73+
-->
74+
75+
[Axum] being objectively the best alternative, we will rewrite the Prose Pod API using
76+
this library. Since the core logic is decoupled from the HTTP [REST] interface, this rewrite
77+
should only affect the `rest-api` crate. By leveraging [Axum]’s ecosystem of middlewares,
78+
the migration should be pretty quick and go smoothly.
79+
80+
We put a lot of effort into writing tests first
81+
(see [ADR: Write tests with the Gherkin syntax] and [ADR: Write integration tests]),
82+
but now is the moment it pays off. We can rewrite the [REST] API stress-free, with the security
83+
of having tests in place to ensure stability and non-regression.
84+
85+
## Consequences
86+
87+
<!--
88+
This section describes the resulting context, after applying the decision.
89+
All consequences should be listed here, not just the "positive" ones.
90+
A particular decision may have positive, negative, and neutral consequences,
91+
but all of them affect the team and project in the future.
92+
-->
93+
94+
This migration will have a positive impact on developer productivity, code readability
95+
and conciseness, maintainability, security, and many other aspects. It will also simplify
96+
the addition of planned features like fine-grained authorization and [observability].
97+
98+
[Axum]’s macro-free API should also reduce compile times, thus improving developer experience.
99+
100+
Edit: After the migration, we noticed a large 32% reduction of compile time in release mode,
101+
and a negligibly smaller binary size
102+
(see the full benchmark in [`prose-pod-api#95` “Migrate from Rocket to Axum”][pr-95]).
103+
104+
On the other side, it’s important to note that:
105+
106+
- [Axum] doesn’t support routing by [`Accept`] header like [Rocket does][rocket-ct-routing]
107+
(see [`axum#1654` “Enable Routing by Content-Type Header”][axum#1654]).
108+
- [The workaround][axum-ct-routing] is quite simple.
109+
- [Rocket] has a nicer API for working with [Server-Sent Events][SSE].
110+
- We’d have to create our own helper functions if we wanted this level of expressivity.
111+
112+
[`2267262`]: https://github.com/prose-im/prose-pod-api/commit/22672622cb31fbd1eb82fb460515a514e1ae9b71
113+
[`Accept`]: https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept "Accept - HTTP | MDN"
114+
[`actix-web#1289`]: https://github.com/actix/actix-web/issues/1289 "Project future · Issue #1289 · actix/actix-web"
115+
[`check_dns_records_route`]: https://github.com/prose-im/prose-pod-api/blob/e3e6bbba82fa0d1934990f878c1db376fc35f7d8/crates/rest-api/src/features/network_checks/check_dns_records.rs#L8-L18
116+
[`hyper`]: https://crates.io/crates/hyper "hyper | crates.io"
117+
[`init_first_account_route`]: https://github.com/prose-im/prose-pod-api/blob/e3e6bbba82fa0d1934990f878c1db376fc35f7d8/crates/rest-api/src/features/init/init_first_account.rs#L31-L50
118+
[`LazyGuard`]: https://github.com/prose-im/prose-pod-api/blob/e3e6bbba82fa0d1934990f878c1db376fc35f7d8/crates/rest-api/src/guards/mod.rs#L35-L47
119+
[`tonic`]: https://crates.io/crates/tonic "tonic | crates.io"
120+
[`tower-http`]: https://crates.io/crates/tower-http "tower-http | crates.io"
121+
[`tower`]: https://crates.io/crates/tower "tower | crates.io"
122+
[`tower`’s documentation]: https://docs.rs/tower/0.5.2/tower/#overview "tower | docs.rs"
123+
[`warp`]: https://crates.io/crates/warp "warp | crates.io"
124+
[Actix Web]: https://crates.io/crates/actix-web "actix-web | crates.io"
125+
[ADR: Write integration tests]: ./2024-05-15-a-integration-testing.md
126+
[ADR: Write tests with the Gherkin syntax]: ./2024-01-11-a-write-tests-in-gherkin.md
127+
[axum#1654]: https://github.com/tokio-rs/axum/issues/1654
128+
[axum-ct-routing]: https://github.com/tokio-rs/axum/issues/1654#issuecomment-1454769195
129+
[Axum]: https://crates.io/crates/axum "axum | crates.io"
130+
[observability]: https://en.wikipedia.org/wiki/Observability_(software) "Observability (software) | Wikipedia"
131+
[pr-95]: https://github.com/prose-im/prose-pod-api/pull/95 "Migrate from Rocket to Axum"
132+
[request guards]: https://rocket.rs/guide/v0.5/requests/#request-guards "Requests > Request Guards - Rocket Web Framework"
133+
[REST]: https://en.wikipedia.org/wiki/REST "REST | Wikipedia"
134+
[rocket-ct-routing]: https://rocket.rs/guide/v0.5/requests/#format "Requests > Format | The Rocket Programming Guide"
135+
[Rocket]: https://rocket.rs/ "Rocket homepage"
136+
[SSE]: https://en.wikipedia.org/wiki/Server-sent_events "Server-sent events | Wikipedia"
137+
[techempower-r22]: https://www.techempower.com/benchmarks/#hw=ph&test=fortune&section=data-r22 "Round 22 results - TechEmpower Framework Benchmarks"
138+
[the Contributors Insight]: https://github.com/actix/actix-web/graphs/contributors?from=31/12/2022 "Contributors to actix/actix-web"
139+
[Tokio]: https://tokio.rs/ "Tokio - An asynchronous Rust runtime"
140+
[“A sad day for Rust”]: https://steveklabnik.com/writing/a-sad-day-for-rust "A sad day for Rust | steveklabnik.com"
141+
[“Announcing Axum”]: https://tokio.rs/blog/2021-07-announcing-axum "Announcing Axum | Tokio - An asynchronous Rust runtime"
142+
[“Rocket is dead. (?)” on r/rust]: https://www.reddit.com/r/rust/comments/zvvrl7/rocket_is_dead/
143+
144+
[^lloc]: Logical source lines of code. See [“Source lines of code” on Wikipedia](https://en.wikipedia.org/wiki/Source_lines_of_code).
145+
[^needs]: I’m not saying Rocket is a bad framework, it really is a great one, but it doesn’t fit **our** needs _at the moment_.

ADRs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ as a template and replace every occurrence of `<TODO:Whatever>` by whatever it s
2121
- [Use `prose-xmpp` and `mod_rest` to send stanzas to Prosody](./2024-06-05-a-prose-xmpp-and-mod_rest-to-send-stanzas.md) (2024-06-05)
2222
- [Store workspace data in a vCard](./2024-07-14-a-store-workspace-data-in-xmpp-vcard.md) (2024-07-14)
2323
- [Rotate service accounts passwords at every startup](./2024-07-16-a-rotate-service-passwords.md) (2024-07-16)
24+
- [Use Axum instead of Rocket](./2025-01-01-a-use-axum-instead-of-rocket.md) (2025-01-01)
2425

2526
## Proposed ADRs
2627

0 commit comments

Comments
 (0)