perf(round29): cache-serve borrow-probe, NC REPORT href buffer, auth per-req allocs, DB over-fetch
Seven behaviour-preserving allocation / copy / bandwidth cuts, each behind a counting-allocator BEFORE/AFTER gate that exit(1)s unless AFTER allocates strictly fewer than BEFORE (benches/ROUND29.md, examples/bench_round29_micro.rs). - [B] Content-cache serve fast path (optimized_inner Tier 1 + get_file_range_preloaded — the video-scrub hot path): probe the cache with a borrow first and build the owned get_or_load args (quoted-etag / key / id Strings) only on a miss, instead of allocating them before every probe and discarding them on a hit. 6 -> 0 allocs per cache hit. Splits get_or_load into get + load_and_cache so the miss path is not re-probed and the hit/miss stat counters stay byte-identical. Also drops the unconditional content_hash/name clones that ran for the >=10 MB streaming tier that used neither. - [A] NextCloud REPORT emit loops: per-row href String (and format! per folder row) -> one reused href_buf via nc_href_into / nc_collection_href_into with the URL-encoded user computed once per page. 1497 fewer allocs on a 500-row page. - [C] read_full: a single-frame blob is returned zero-copy instead of a second whole-payload memcpy into a fresh BytesMut; multi-frame path unchanged. - [D] login-lockout key: to_lowercase()+format! -> one pre-sized ASCII buffer (non-ASCII keeps str::to_lowercase). 3 -> 1 alloc/req, byte-identical key. - [E] NC composite-username parse: owned clone/to_string -> &str borrow of the already-owned raw_username. 1 -> 0 alloc on the common no-marker path. - [F] get_contacts_in_group: stop SELECTing the discarded multi-KB vcard column (the live method ROUND25 §Q2 missed; ContactDto has no vcard field). - [G] count_admin_users: add count_users_by_role -> scalar COUNT(*) instead of hydrating every admin's full row (incl. up-to-512 KiB avatar + ui_preferences JSONB) only to .len() it, on a bootstrap-polled status endpoint. All seven gates pass; cargo fmt --check and cargo clippy --all-features --all-targets -D warnings clean. §F/§G additionally validated against a live PostgreSQL 16 with the full migration set (query validity, result equivalence, 600000 -> 8 byte wire delta on the admin count). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhpDZxSQTAGnAqCHUdtG5N
This commit is contained in:
@@ -109,7 +109,12 @@ pub async fn basic_auth_middleware(
|
||||
// at the auth boundary rather than treating them as "missing
|
||||
// marker" — they are unambiguous typos that would otherwise
|
||||
// silently fall into a different code path.
|
||||
let (username, drive_marker): (String, Option<String>) = match raw_username.split_once('~') {
|
||||
// Borrow the prefix / marker out of the already-owned `raw_username`
|
||||
// (`split_once` yields `&str` slices) instead of allocating a duplicate
|
||||
// `String` per request — `username` is only ever passed by reference, and
|
||||
// `raw_username` outlives every use before it moves into `NcSession`
|
||||
// (benches/ROUND29.md §E).
|
||||
let (username, drive_marker): (&str, Option<&str>) = match raw_username.split_once('~') {
|
||||
Some(("", _)) => {
|
||||
tracing::warn!(
|
||||
"[NC] 401 malformed composite username (empty prefix): {}",
|
||||
@@ -124,15 +129,15 @@ pub async fn basic_auth_middleware(
|
||||
);
|
||||
return Err(NextcloudAuthError::Unauthorized);
|
||||
}
|
||||
Some((u, m)) => (u.to_string(), Some(m.to_string())),
|
||||
None => (raw_username.clone(), None),
|
||||
Some((u, m)) => (u, Some(m)),
|
||||
None => (raw_username.as_str(), None),
|
||||
};
|
||||
|
||||
// Check account lockout before attempting password verification (saves CPU).
|
||||
// The lockout is per (account, IP), see #323 for rationale.
|
||||
let client_ip = crate::interfaces::middleware::rate_limit::extract_client_ip(&request);
|
||||
if let Some(auth_svc) = state.auth_service.as_ref()
|
||||
&& let Err(secs) = auth_svc.login_lockout.check(&username, &client_ip)
|
||||
&& let Err(secs) = auth_svc.login_lockout.check(username, &client_ip)
|
||||
{
|
||||
tracing::warn!(
|
||||
username = %username,
|
||||
@@ -150,13 +155,13 @@ pub async fn basic_auth_middleware(
|
||||
|
||||
match nextcloud
|
||||
.app_passwords
|
||||
.verify_basic_auth(&username, &password)
|
||||
.verify_basic_auth(username, &password)
|
||||
.await
|
||||
{
|
||||
Ok((user_id, uname, email, role)) => {
|
||||
// Reset lockout counter on success
|
||||
if let Some(auth_svc) = state.auth_service.as_ref() {
|
||||
auth_svc.login_lockout.record_success(&username, &client_ip);
|
||||
auth_svc.login_lockout.record_success(username, &client_ip);
|
||||
}
|
||||
// External users must never authenticate against the NC
|
||||
// surface — that whole subtree (WebDAV files, uploads,
|
||||
@@ -222,7 +227,7 @@ pub async fn basic_auth_middleware(
|
||||
// is the right one: name-independent, secondary-drive-safe.
|
||||
use crate::application::ports::folder_ports::FolderUseCase;
|
||||
use crate::domain::repositories::drive_repository::DriveRepository;
|
||||
let chroot = match drive_marker.as_deref() {
|
||||
let chroot = match drive_marker {
|
||||
None => {
|
||||
match state
|
||||
.drive_repo
|
||||
@@ -287,7 +292,7 @@ pub async fn basic_auth_middleware(
|
||||
Err(_) => {
|
||||
// Record failed attempt for lockout tracking
|
||||
if let Some(auth_svc) = state.auth_service.as_ref() {
|
||||
auth_svc.login_lockout.record_failure(&username, &client_ip);
|
||||
auth_svc.login_lockout.record_failure(username, &client_ip);
|
||||
}
|
||||
Err(NextcloudAuthError::Unauthorized)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user