Skip to content

Fix pool claim race condition - #10301

Open
cetra3 wants to merge 2 commits into
apache:mainfrom
pydantic:fix_claim_race_condition
Open

Fix pool claim race condition#10301
cetra3 wants to merge 2 commits into
apache:mainfrom
pydantic:fix_claim_race_condition

Conversation

@cetra3

@cetra3 cetra3 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

This adjusts the internals of the memory pool reservation so that there isn't any double accounting when replacing memory with claim

What changes are included in this PR?

Adds a new struct in pool.rs which is used to track reservations on buffers. This is essentially a newtype lift of the existing structure, but with a few specialised methods to ensure that claim accounts correctly.

Are these changes tested?

Yes a new test has been added

Are there any user-facing changes?

No this is just some memory pool accounting internals

@github-actions github-actions Bot added the arrow Changes to the arrow crate label Jul 8, 2026
@cetra3
cetra3 force-pushed the fix_claim_race_condition branch from 3da18aa to 4f290aa Compare July 8, 2026 02:54

@Rich-T-kid Rich-T-kid 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.

I haven't taken a look at the code yet but would it be possible to solve the race condition by replacing

pub fn claim(&self, pool: &dyn MemoryPool) {
    *self.reservation.lock().unwrap() = Some(pool.reserve(self.capacity()));
}

with just

pub fn claim(&self, pool: &dyn MemoryPool) {
    let guard = self.reservation.lock().unwrap();
    drop(*guard.take());
    *guard = pool.reserve(self.capacity());
}

like you mentioned in the issue. Why does TrackedReservation need to be introduced?

@cetra3

cetra3 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Yes that is essentially what this PR does. But instead of duplicating that exact same bit of code everywhere, we lift it into a new struct that does it correctly every time & makes it easy for other things to get it right.

@Rich-T-kid

Copy link
Copy Markdown
Contributor

Yes that is essentially what this PR does. But instead of duplicating that exact same bit of code everywhere, we lift it into a new struct that does it correctly every time & makes it easy for other things to get it right.

@cetra3 okay nice. Ill try and take a look tomorrow

@Rich-T-kid Rich-T-kid 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.

LGTM

left a single nit

Comment thread arrow-buffer/src/bytes.rs
Comment on lines -121 to -128
let mut guard = self.reservation.lock().unwrap();
if let Some(mut reservation) = guard.take() {
// Resize the reservation
reservation.resize(new_size);

// Put it back
*guard = Some(reservation);
}

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.

nice

Comment thread arrow-buffer/src/pool.rs
Comment on lines -26 to -28
//! (pool tracker) (resizable)
//! (pool tracker) (resizable)
//! ┌──────────────────┐ fn reserve() ┌─────────────────────────┐
//! │ trait MemoryPool │────────────►│ trait MemoryReservation │

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.

Nit: there are a couple of spots in this PR where nothing has actually changed but a diff is still being shown. We should generally try to avoid that.

Comment thread arrow-buffer/src/pool.rs
}

#[test]
fn test_claim_reclaims_before_reserving() {

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.

nice regression test

@Rich-T-kid

Copy link
Copy Markdown
Contributor

Yes that is essentially what this PR does. But instead of duplicating that exact same bit of code everywhere, we lift it into a new struct that does it correctly every time & makes it easy for other things to get it right.

this PR makes those call sites much more readable

@alamb alamb 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 @cetra3 and @Rich-T-kid

@waynexia can you please help review this PR as you contributed the initial memory pool infrstructure in #7303 ?

Comment thread arrow-buffer/src/pool.rs Outdated
/// This is a wrapper for the reservation so we can standardize on changing
/// and avoid race conditions in memory accounting
#[derive(Debug, Default)]
pub struct TrackedReservation {

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.

Is this pub in the sense that it can be used outside this crate? If so I think we should document it more, and be clear if we need to expand the public API -- if it is meant to be internal, perhaps we can mark it as pub(crate)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've moved this to pub(crate). If people find it useful we can adjust but it is really an internal only thing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow Changes to the arrow crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claim API Race Condition when reclaiming existing memory

3 participants