Skip to main content

Memory

Struct Memory 

Source
pub struct Memory { /* private fields */ }
Expand description

A budget shared by everything running against one database.

Cheap to clone, and a clone shares the total with the budget it came from, so two queries running at once are held to one limit between them rather than to one each. That is what DuckDB’s memory_limit means and it is the only reading that is any use: a limit that each query gets a fresh copy of is not a limit on the process.

§What it counts

What an operator says it is holding. Nothing here hooks the allocator, so the number is the sum of what the buffering operators reserved and not the resident size of the process. The gap is real and it is in one direction, since an operator charges for what it asked for and never for more.

How large the gap is decides whether the limit is any use. Under reporting is the safe direction only while it is small: a budget that is spent at two fifths of the real footprint is not a conservative limit, it is a limit that lets a query take two and a half times what it was allowed and get killed from outside anyway, which is exactly what it was there to prevent. #227 found the aggregate doing that and it is why the operators charge a container for its capacity rather than its length and add ALLOCATION per block. Memory::peak is the accounted side of that comparison, so the gap can be measured rather than assumed.

The operators that reserve are the ones that buffer without bound, which is sorting, grouping, duplicate elimination, joining, set operations and the result a query hands back. A streaming operator holds one chunk and gives it away again, so charging it would be counting the same megabyte once per level of the tree.

§Why a reservation rather than a pair of calls

Memory::reserve hands back a Reservation that releases what it took when it is dropped, so an operator that fails halfway through, or a query stopped by an interrupt, gives its memory back without anybody writing the release. A pair of take and give calls is the version where the release is missed on the error path, and the error path here is the one that matters, since running out of memory is itself an error and it unwinds through every operator below.

Implementations§

Source§

impl Memory

Source

pub fn unlimited() -> Self

A budget nothing is refused against, which still counts what is held.

The counting is kept because Memory::used is worth reading whether or not there is a limit, and because a query that behaves differently depending on whether a limit is set is a query whose limit cannot be tested by setting one.

Source

pub fn with_limit(bytes: u64) -> Self

A budget of this many bytes.

Source

pub fn new(limit: Option<u64>) -> Self

A budget of this many bytes, or no limit at all.

Source

pub fn limit(&self) -> Option<u64>

The limit, if there is one.

Source

pub fn set_limit(&self, limit: Option<u64>)

Changes the limit, for every query holding this budget.

A limit below what is already held is allowed and refuses the next reservation rather than stopping anything, which is what DuckDB does and is the only behaviour that does not turn a setting into a way of killing whatever happens to be running.

Source

pub fn used(&self) -> u64

How many bytes are held right now.

Source

pub fn peak(&self) -> u64

The most that was ever held at once since the last Memory::forget_peak.

Memory::used falls back to zero when a query ends, so it answers what is held and never what was held, and what was held is the number worth knowing. It is what a query cost, it is what has to be compared against the resident set to find out whether the accounting means anything, and it is the one to print beside a benchmark row.

It is a property of the budget rather than of a query, so two queries running at once share one and it is the peak of the pair.

Source

pub fn forget_peak(&self)

Puts the high water mark back to what is held right now.

Back to what is held rather than to zero, because a mark below the current total would be a number that says less was held than is held.

Source

pub fn reservation(&self) -> Reservation

A reservation on this budget that is holding nothing yet.

What a buffering operator starts with, because it is built before it has read anything and its constructor has no error to report. It grows as the input arrives.

Source

pub fn reserve(&self, bytes: u64) -> Result<Reservation>

Takes bytes out of the budget, to be given back when the reservation is dropped.

Reserving nothing always works and is the way an operator gets a handle it can grow later.

§Errors

crate::ErrorCode::OutOfMemory when the limit is set and this would pass it. Nothing is taken in that case, so a caller that carries on after catching it is holding what it held before.

Trait Implementations§

Source§

impl Clone for Memory

Source§

fn clone(&self) -> Memory

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Memory

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Memory

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.