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:
@@ -24,8 +24,8 @@ use crate::interfaces::api::handlers::webdav_handler::{
|
||||
};
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::nextcloud::webdav_handler::{
|
||||
batch_resolve_ids, format_oc_id_into, nc_href, nc_id_of, write_file_response,
|
||||
write_folder_response,
|
||||
batch_resolve_ids, format_oc_id_into, nc_collection_href_into, nc_href_into, nc_id_of,
|
||||
write_file_response, write_folder_response,
|
||||
};
|
||||
|
||||
/// Handle WebDAV REPORT and SEARCH methods for Nextcloud compatibility.
|
||||
@@ -177,6 +177,12 @@ async fn handle_filter_files(
|
||||
// owner-id stays canonical via `&user.username`.
|
||||
// One oc:id buffer reused across both emit loops (benches/ROUND27.md §H1).
|
||||
let mut oc_buf = String::new();
|
||||
// One href buffer reused across both emit loops, with the URL-encoded
|
||||
// user computed once for the page instead of re-encoded per row — the
|
||||
// reused-buffer shape the PROPFIND child loop already uses
|
||||
// (benches/ROUND29.md §A).
|
||||
let encoded_user = urlencoding::encode(url_user);
|
||||
let mut href_buf = String::new();
|
||||
for file in &files {
|
||||
// Skip favorites that live outside the caller's chroot
|
||||
// (other-drive favorites); reachable via REST if needed.
|
||||
@@ -189,7 +195,7 @@ async fn handle_filter_files(
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let href = nc_href(url_user, subpath);
|
||||
nc_href_into(&mut href_buf, &encoded_user, subpath);
|
||||
let fid = nc_id_of(&file_id_map, &file.id);
|
||||
let oc_id: Option<&str> = match fid {
|
||||
Some(id) => {
|
||||
@@ -202,7 +208,7 @@ async fn handle_filter_files(
|
||||
write_file_response(
|
||||
&mut xml,
|
||||
file,
|
||||
&href,
|
||||
&href_buf,
|
||||
(fid, oc_id),
|
||||
&user.username,
|
||||
&favorite_ids,
|
||||
@@ -221,7 +227,7 @@ async fn handle_filter_files(
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let href = format!("{}/", nc_href(url_user, subpath));
|
||||
nc_collection_href_into(&mut href_buf, &encoded_user, subpath);
|
||||
let fid = nc_id_of(&folder_id_map, &folder.id);
|
||||
let oc_id: Option<&str> = match fid {
|
||||
Some(id) => {
|
||||
@@ -234,7 +240,7 @@ async fn handle_filter_files(
|
||||
write_folder_response(
|
||||
&mut xml,
|
||||
folder,
|
||||
&href,
|
||||
&href_buf,
|
||||
(fid, oc_id),
|
||||
&user.username,
|
||||
&favorite_ids,
|
||||
@@ -334,6 +340,12 @@ async fn handle_search(
|
||||
// Files.
|
||||
// One oc:id buffer reused across both emit loops (benches/ROUND27.md §H1).
|
||||
let mut oc_buf = String::new();
|
||||
// One href buffer reused across both emit loops, with the URL-encoded
|
||||
// user computed once for the page instead of re-encoded per row — the
|
||||
// reused-buffer shape the PROPFIND child loop already uses
|
||||
// (benches/ROUND29.md §A).
|
||||
let encoded_user = urlencoding::encode(url_user);
|
||||
let mut href_buf = String::new();
|
||||
for file in &files {
|
||||
let Some(subpath) = strip_home_prefix(chroot, &file.path, home_prefix) else {
|
||||
tracing::debug!(
|
||||
@@ -344,7 +356,7 @@ async fn handle_search(
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let href = nc_href(url_user, subpath);
|
||||
nc_href_into(&mut href_buf, &encoded_user, subpath);
|
||||
let fid = nc_id_of(&file_id_map, &file.id);
|
||||
let oc_id: Option<&str> = match fid {
|
||||
Some(id) => {
|
||||
@@ -357,7 +369,7 @@ async fn handle_search(
|
||||
write_file_response(
|
||||
&mut xml,
|
||||
file,
|
||||
&href,
|
||||
&href_buf,
|
||||
(fid, oc_id),
|
||||
&user.username,
|
||||
&favorite_ids,
|
||||
@@ -377,7 +389,7 @@ async fn handle_search(
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let href = format!("{}/", nc_href(url_user, subpath));
|
||||
nc_collection_href_into(&mut href_buf, &encoded_user, subpath);
|
||||
let fid = nc_id_of(&folder_id_map, &folder.id);
|
||||
let oc_id: Option<&str> = match fid {
|
||||
Some(id) => {
|
||||
@@ -390,7 +402,7 @@ async fn handle_search(
|
||||
write_folder_response(
|
||||
&mut xml,
|
||||
folder,
|
||||
&href,
|
||||
&href_buf,
|
||||
(fid, oc_id),
|
||||
&user.username,
|
||||
&favorite_ids,
|
||||
|
||||
Reference in New Issue
Block a user