Skip to content

NCBC-4383: Dispose the builder pools in the MutateIn boundary test - #240

Merged
davidkelly merged 2 commits into
masterfrom
NCBC-4383
Oct 30, 2026
Merged

davidkelly merged 2 commits into
masterfrom
NCBC-4383

Conversation

@davidkelly

@davidkelly davidkelly commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

The issue in brief

A unit test added by NCBC-4376 borrows buffers from a shared pool and never gives them back. On .NET Framework, the shared pool keeps count of buffers that are out. After this test it thinks the 32 KiB and 64 KiB sizes are all in use, so later requests for 64 KiB get a 128 KiB buffer.

Some later tests check the exact buffer size, so they then fail on the Windows net48 legs. Whether they fail depends on which test classes run first. This made OperationBuilderTests fail on master (run 40765292781) and three times in a row on PR #239. Production code is not affected: the SDK's own builder pool disposes builders it drops.

Change

SendAsync_FirstSpecEndsInLastEightBytesOfBuffer_DoesNotThrow still makes a new pool for each of its 200 iterations, so every send starts from a buffer that has not grown. The pool now comes from DefaultObjectPoolProvider, which disposes the builders it holds, and the test disposes it after each send. One shared pool would not work: Reset() keeps the grown capacity, so the test would stop reaching the buffer boundary it checks.

The EnsureCapacity growth tests in OperationBuilderTests (moved here from #239, as asked in review) now assert that the capacity is at least the requested size, not an exact size. The exact size came from the shared pool, not from the builder, so a test that holds pool buffers could still change it. The four tests are renamed from *_Doubles and *_Quadruples to *_Grows.

Tests

  • I wrapped ArrayPool<byte>.Shared with a probe and ran the net48 tests on Mono. Before the change, this test left 50 64 KiB buffers out for the rest of the process. After it, none.
  • With the NCBC-4376 fix removed from OperationBuilder, the test still fails.
  • MultiMutationTests pass on net8.0, and on net48 under Mono. MultiMutationTests and OperationBuilderTests pass on net10.0.

Jira: NCBC-4383

🤖 Generated with Claude Code

Motivation
==========
SendAsync_FirstSpecEndsInLastEightBytesOfBuffer_DoesNotThrow creates a
DefaultObjectPool on each of its 200 iterations. That pool never
disposes its builders, so their ArrayPool<byte>.Shared arrays were
never returned. On net48 the pool counts rented slots and serves the
next larger bucket once a bucket is fully rented, so the 32 KiB and
64 KiB buckets stayed full. Tests that ran later and rented 64 KiB got
131072 bytes, and OperationBuilderTests failed on Windows net48,
depending on test order.

Modification
============
Create each iteration's pool with DefaultObjectPoolProvider and dispose
it after the send. The pool stays per iteration: a shared one would
keep the grown buffer, and the test would no longer reach the boundary
it checks.

Results
=======
The test returns every buffer it rents and still fails without the
NCBC-4376 fix.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues were identified for the test-only change to dispose each iteration’s builder pool.

No open issues 🎉

What changed in this PR

Fixes buffer-pool cleanup in the MutateIn boundary regression test, preventing test-order-dependent buffer-size failures on .NET Framework without changing production code.

Changes:

  • Creates each iteration’s builder pool through DefaultObjectPoolProvider.
  • Disposes the pool in finally, returning pooled buffers while preserving a fresh pool for each boundary check.
File Description
tests/​Couchbase.UnitTests/​Core/​IO/​Operations/​SubDocument/​MultiMutationTests.cs Disposes each iteration’s builder pool to prevent shared-buffer leaks.

Review effort: Balanced

@emilienbev emilienbev left a comment

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.

Thanks, this looks good to me. Could the lower-bound asserts in OperationBuilderTests from #239 move here? They fix the same flaky tests.

@davidkelly

Copy link
Copy Markdown
Contributor Author

They could. In fact should really. I'll do that.

Motivation
==========
The EnsureCapacity growth tests asserted the exact capacity, which is
the size of the array that ArrayPool<byte>.Shared returns, not a value
the builder chooses. On net48 the pool serves the next larger bucket
once all slots of the exact bucket are rented, so any test that holds
64 KiB buffers can make the *_Quadruples tests see 131072.

Modification
============
Assert that the capacity is at least the requested size. Rename the
four tests from *_Doubles and *_Quadruples to *_Grows.

Results
=======
The tests no longer depend on what other tests rent from the shared
pool.
@davidkelly

Copy link
Copy Markdown
Contributor Author

Moved the lower-bound asserts here as e695430, and dropped them from #239.

@davidkelly
davidkelly merged commit 8d18a38 into master Oct 30, 2026
25 checks passed
@davidkelly
davidkelly deleted the NCBC-4383 branch October 30, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants