-
-
Notifications
You must be signed in to change notification settings - Fork 249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Definite memory leak when calling dynamic function #1341
Comments
Thanks for the report. We’ll investigate |
I rewrote this to be even more aggressive: #[pg_extern]
fn tester() {
let bar = "bar";
for _ in 0..10000000 {
check_for_interrupts!();
let s = fn_call::<String>("myfunc", &[&Arg::Value(bar)]);
assert!(matches!(s, Ok(Some(_))))
}
}
#[pg_extern]
fn myfunc(bar: &str) -> String {
static CAPACITY: usize = 1024 * 1024 * 500; // 500 meg
let mut s = String::with_capacity(CAPACITY);
while s.len() < CAPACITY {
s.push_str(bar)
}
s
} What's happening here is that when Anyways, that, what is now an intermediate palloc'd As such, it's the user's responsibility to manage that memory, in an #[pg_extern]
fn tester() {
let bar = "bar";
for _ in 0..10000000 {
check_for_interrupts!();
unsafe {
PgMemoryContexts::switch_to(
&mut PgMemoryContexts::Transient {
parent: PgMemoryContexts::CurrentMemoryContext.value(),
name: "per-call",
min_context_size: 4096,
initial_block_size: 4096,
max_block_size: 4096,
},
|_| {
let s = fn_call::<String>("myfunc", &[&Arg::Value(bar)]);
assert!(matches!(s, Ok(Some(_))));
},
)
}
}
} This pattern is common in the Postgres sources, and is the reason something like the below doesn't leak -- there's a temporary memory context that manages the allocations within the FOR loop's scope. create function make_big_string(s text) returns text language sql AS $$
SELECT repeat(s, (1024 * 1024 * 500) / length(s))
$$;
DO LANGUAGE plpgsql $$
BEGIN
FOR i in 0..10000000 LOOP
PERFORM make_big_string('bar');
END LOOP;
RETURN;
END;
$$; I don't know what pgrx or |
This seems like basically the same problem as with SPI: we want to operate in a temporary context, but then return the result into a longer-lived context. This could be safely done, possibly by accepting arguments to |
If And really, I don't see how any of our existing "owned datum" approaches can survive this. Like, creating a Rust Then it makes sense that |
Thinking on this issue a bit more, I can have it create a temporary context to execute the function in, and use our Thoughts? |
Some thoughts:
|
3 is actively being worked on from different angles. It's an incredibly hard problem, as you can imagine, and a lot of the work is exploratory, trying to mind-meld with Postgres. Regarding 2, we still need to see the exact code you're running for us to help with #1330. If pgrx has another leak somewhere, I'd like to know. FWIW, when implementing this "fn_call" feature, I did consider a "prepared" version that only needed to do the function lookup and such once, but decided to hold off on that until users thought such a thing would be helpful. So for 1, yes, I think the general use case for "fn_call" is exactly that -- just call some other function once. FWIW, if "fn_call" is used via a Knowing this, I'm not super motivated to make more changes here. There's lots of ways to leak memory in Rust and in Postgres. |
In an attempt to diagnose #1330 I installed the new 0.11.0 and tried out the new dynamic function calls. I got a leak. Perhaps a fix for this one will also fix my issue.
Run this and watch memory usage climb to multiple gb:
Just run
select tester();
at the command line, and if you're on a Mac open up Activity Monitor / Memory tab and watch the memory usage grow for the postgres process.The text was updated successfully, but these errors were encountered: