Skip to content

🧹 chore: Use sync.Pool for form and multipart binding#3965

Closed
gaby wants to merge 1 commit into
mainfrom
add-sync.pool-instances-for-maps
Closed

🧹 chore: Use sync.Pool for form and multipart binding#3965
gaby wants to merge 1 commit into
mainfrom
add-sync.pool-instances-for-maps

Conversation

@gaby
Copy link
Copy Markdown
Member

@gaby gaby commented Dec 24, 2025

Summary

  • reuse pooled maps for form values and multipart file headers in the form binder, clearing them on acquire and release to reduce allocations
  • update binding logic to return pooled maps after parsing while keeping multipart and urlencoded handling unchanged
  • add regression tests confirming pooled form and multipart maps are cleared between requests

Related #3767

Copilot AI review requested due to automatic review settings December 24, 2025 13:23
@gaby gaby requested a review from a team as a code owner December 24, 2025 13:23
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 24, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The PR introduces object pooling for form data maps in the binder package to reduce memory allocations during form binding operations. Maps are acquired from pools before use and released after, with proper clearing to prevent cross-request data leakage.

Changes

Cohort / File(s) Summary
Form binding pooling implementation
binder/form.go
Introduces sync.Pool instances for map[string][]string and map[string][]*multipart.FileHeader. Refactors Bind() and bindMultipart() to acquire pooled maps via helper functions (acquireFormMap, acquireFileHeaderMap) and release them after binding. Adds cleanup functions (clearFormMap, clearFileHeaderMap) to reset maps before reuse and prevent data leakage across requests.
Form binding isolation tests
binder/form_test.go
Adds two integration tests verifying per-request isolation: Test_FormBinder_Bind_MapClearedBetweenRequests confirms URL-encoded form maps are cleared between sequential requests; Test_FormBinder_BindMultipart_MapsClearedBetweenRequests verifies multipart form state (including file headers) does not persist across separate requests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

🧹 Updates, v3, ⚡️ Performance

Suggested reviewers

  • sixcolors
  • ReneWerner87
  • efectn

Poem

🐰 Maps dance in pools so neat,
Reused with care, a recycled treat,
No leaks between each request's beat,
Form binding springs with pooling's feat! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive The description covers the core objective (pooling maps to reduce allocations) and includes testing details, but does not follow the required template structure with sections like 'Changes introduced', 'Type of change', and 'Checklist'. Restructure the description to match the repository template by adding explicit sections for 'Changes introduced', marking the 'Type of change' (e.g., 'Performance improvement'), and completing the 'Checklist' items where applicable.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'pool form binder maps' directly describes the main change—introducing synchronization pools for form binder maps to reduce allocations.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-sync.pool-instances-for-maps

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @gaby, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements an optimization to the form binding mechanism by introducing object pooling for the internal maps used to store form values and multipart file headers. By reusing these maps across requests, the change aims to reduce memory allocations and garbage collector pressure, thereby improving the overall performance of the binder. The update ensures that all pooled maps are properly cleared before reuse to maintain data integrity and prevent unintended data leakage between different binding operations.

Highlights

  • Performance Optimization: Introduced sync.Pool for map[string][]string (form values) and map[string][]*multipart.FileHeader (multipart file headers) to reuse map objects, significantly reducing memory allocations during form binding.
  • Binding Logic Update: Modified the FormBinding.Bind and FormBinding.bindMultipart methods to acquire maps from these pools at the start of binding and release them back to the pools after processing, ensuring efficient resource management.
  • Map Clearing Mechanism: Implemented helper functions (acquireFormMap, releaseFormMap, acquireFileHeaderMap, releaseFileHeaderMap, clearFormMap, clearFileHeaderMap) to ensure that pooled maps are thoroughly cleared before being reused, preventing data contamination between requests.
  • Regression Tests: Added new regression tests (Test_FormBinder_Bind_MapClearedBetweenRequests and Test_FormBinder_BindMultipart_MapsClearedBetweenRequests) to explicitly verify that the pooled form and multipart maps are correctly cleared between subsequent requests.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 24, 2025

Codecov Report

❌ Patch coverage is 88.88889% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.60%. Comparing base (032ff5f) to head (514bca4).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
binder/form.go 88.88% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3965      +/-   ##
==========================================
- Coverage   91.67%   91.60%   -0.08%     
==========================================
  Files         119      119              
  Lines       10177    10227      +50     
==========================================
+ Hits         9330     9368      +38     
- Misses        536      544       +8     
- Partials      311      315       +4     
Flag Coverage Δ
unittests 91.60% <88.88%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces sync.Pool for form value maps and multipart file headers to reduce memory allocations, which is a good performance optimization. The changes are accompanied by regression tests to ensure maps are cleared between requests.

My review identified a critical issue where a map is returned to the wrong pool, which would lead to memory leaks and negate some of the pooling benefits. I've also suggested a few improvements for maintainability and minor performance gains, such as using the clear() built-in function and removing redundant map clearing operations.

Overall, this is a valuable change once the critical issue is addressed.

Comment thread binder/form.go

func releaseFileHeaderMap(m map[string][]*multipart.FileHeader) {
clearFileHeaderMap(m)
formFileMapPool.Put(m)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There appears to be a copy-paste error. The fileHeaderMap is being put into formMapPool instead of formFileMapPool. This will cause the type assertion to fail in acquireFormMap, negating the pooling benefit for form maps, and will cause a memory leak as file header maps are never returned to their correct pool.

Suggested change
formFileMapPool.Put(m)
formFileMapPool.Put(m)

Comment thread binder/form.go
if !ok {
m = make(map[string][]string)
}
clearFormMap(m)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The map is cleared in releaseFormMap before being returned to the pool. This additional clear upon acquisition is redundant. While it adds a layer of safety, it's generally sufficient to ensure resources are cleaned on release. Removing this would be a minor performance optimization, which aligns with the goal of this PR.

Comment thread binder/form.go
if !ok {
m = make(map[string][]*multipart.FileHeader)
}
clearFileHeaderMap(m)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to acquireFormMap, this call to clearFileHeaderMap is redundant because the map is already cleared in releaseFileHeaderMap before being put back into the pool. Removing it would be a small performance gain.

Comment thread binder/form.go
Comment thread binder/form.go
@gaby gaby changed the title 🧹 chore: pool form binder maps 🧹 chore: Use sync.Pool for form binder maps Dec 24, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces memory pooling for form and multipart binding to reduce allocations during request processing. The implementation adds sync.Pool instances for form value maps and multipart file header maps, with acquire/release helper functions that ensure maps are properly cleared between uses.

Key Changes:

  • Added pooled map storage for form values and multipart file headers using sync.Pool
  • Modified binding methods to acquire maps from pools and release them after parsing
  • Added regression tests confirming maps are properly cleared between requests

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
binder/form.go Implements pool infrastructure with acquire/release/clear functions for form and file header maps; integrates pooling into Bind and bindMultipart methods
binder/form_test.go Adds regression tests verifying pooled maps are cleared between requests for both urlencoded and multipart forms

@gaby gaby changed the title 🧹 chore: Use sync.Pool for form binder maps 🧹 chore: Use sync.Pool for form and multipart binding Dec 24, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
binder/form.go (1)

92-130: The double-clearing pattern ensures data isolation; consider clear() builtin for Go 1.21+.

The belt-and-suspenders approach of clearing on both acquire and release is sound for preventing cross-request data leakage. The defensive type assertion fallback (lines 94-96, 108-110) is good practice.

If the minimum supported Go version is 1.21+, you could simplify clearFormMap and clearFileHeaderMap using the clear() builtin:

func clearFormMap(m map[string][]string) {
    clear(m)
}

Otherwise, the current range-delete pattern is correct for broader compatibility. Note: github.com/gofiber/utils/v2 does not provide a map clearing helper, so the current approach aligns with standard Go patterns.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93830f2 and 514bca4.

📒 Files selected for processing (2)
  • binder/form.go
  • binder/form_test.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

When adding Go tests, always invoke t.Parallel() at the start of each test and subtest to maximize concurrency

Files:

  • binder/form_test.go
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Prefer github.com/gofiber/utils/v2 helpers (for example, utils.Trim) when performing common operations such as string manipulation, whenever it is practical and appropriate for the surrounding code

Files:

  • binder/form_test.go
  • binder/form.go
🧠 Learnings (1)
📚 Learning: 2024-12-01T10:28:36.011Z
Learnt from: ReneWerner87
Repo: gofiber/fiber PR: 0
File: :0-0
Timestamp: 2024-12-01T10:28:36.011Z
Learning: Fiber currently supports square bracket notation in form data with 'application/x-www-form-urlencoded' content type, but this feature is not available for multipart form data. The implementation for form-urlencoded is in ctx.go (BodyParser method), while multipart form data handling needs enhancement to support this feature.

Applied to files:

  • binder/form.go
🧬 Code graph analysis (2)
binder/form_test.go (3)
binder/form.go (2)
  • FormBinding (27-29)
  • FormBinding (32-34)
client/request.go (2)
  • AcquireRequest (983-989)
  • ReleaseRequest (993-996)
bind.go (1)
  • Bind (33-37)
binder/form.go (1)
app.go (1)
  • New (522-635)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Agent
  • GitHub Check: lint
  • GitHub Check: Compare
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: repeated
🔇 Additional comments (4)
binder/form.go (2)

43-54: Correct pool usage with defer.

The defer releaseFormMap(data) ensures the pooled map is returned to the pool on all code paths, including early error returns from formatBindData.


64-84: Pool usage is consistent and correct for multipart handling.

Both the form data map and file header map are properly acquired from pools and released via defer, ensuring cleanup on all code paths.

binder/form_test.go (2)

241-270: Well-structured regression test for URL-encoded form isolation.

The test properly validates that pooled maps are cleared between requests, ensuring the second request doesn't inherit values from the first. Good use of t.Parallel() per coding guidelines.


272-322: Comprehensive regression test for multipart form isolation.

Excellent coverage of both value and file map isolation. The test ensures that file headers (Avatar) and form values (Name) from the first request don't bleed into the second request. Good use of t.Parallel() per coding guidelines.

@gaby gaby closed this Dec 24, 2025
@github-project-automation github-project-automation Bot moved this to Done in v3 Dec 24, 2025
@ReneWerner87 ReneWerner87 deleted the add-sync.pool-instances-for-maps branch January 27, 2026 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants