Skip to content

Optimize sanitizer hot paths for high-throughput response conversion #3117

Description

@SamMorrowDrums

Describe the bug

Sanitization is now applied consistently across user-authored response fields, but the current pipeline performs repeated rune conversion and multiple full-string passes. At high request rates this becomes allocation- and GC-bound on list/comment-heavy responses.

Measured examples on current main:

  • 100 comments × 1 KiB: about 3.35 ms and 2.06 MiB allocated per conversion.
  • 30 issues with 2 KiB bodies: about 2.14 ms and 1.30 MiB allocated.
  • A 64 KiB body: about 2.38 ms and 1.21 MiB allocated.

At roughly 1,250 requests/second, workloads dominated by these shapes can require several CPU cores and produce multi-gigabyte/second allocation rates.

Affected version

Current main as of 2026-08-19, after the sanitizer coverage and post-normalization Unicode filtering changes.

Steps to reproduce the behavior

  1. Benchmark sanitize.Sanitize for short titles, 1 KiB comments, and 64 KiB bodies.
  2. Benchmark representative minimal-converter pages containing 30 issues or 100 comments.
  3. Compare CPU time and allocations with the pre-sanitization converter paths.
  4. Observe repeated []rune conversion and post-normalization passes dominating CPU and allocations.

Expected vs actual behavior

Expected: sanitization preserves the current output and security policy with negligible overhead for ordinary clean text.

Actual: benign content pays for repeated allocating scans and HTML-policy processing, making high-throughput response conversion GC-bound.

Optimize without weakening behavior:

  • Make invisible-character filtering scan first and return the original string without allocation when no filtered rune exists.
  • Skip the post-HTML invisible/code-fence pass when normalization returned unchanged content.
  • Add only a conservative, proven-safe fast path for clean printable ASCII that cannot contain HTML/entity syntax.
  • Preserve byte-for-byte sanitizer output across a broad corpus, fuzz/property tests, and all existing edge cases.
  • Add committed benchmarks and report before/after CPU and allocation results for representative response shapes.

Logs

N/A

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions