-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Puma raises WARNING: Detected 1 Thread(s) started in app boot with preload_app and workers #37066
Comments
I have the same problem, any update? |
This issue has been automatically marked as stale because it has not been commented on for at least three months. |
Related: #25259 |
Does anyone have more info about this? You still get this error under ActiveRecord 6.0.3.3 and puma 4.3.5, if you have puma configured with heroku's recommendations. (multiple processes with preload, also
puma is warning that a thread is started BEFORE fork happens, so may not exist where expected in forked process. It looks like the thread started is meant to be a "reaper" thread, eliminating abandoned connections from the connection pool. It may not be running in all of your puma processes, meaning some of them may not be reaping abandoned connections. Maybe this isn't a problem, but definitely isn't as intended. Perhaps this should be re-opened? Paging @schneems (hope you don't mind, let me know if you do), since this happens under the recommended heroku puma configuration (which is where I noticed it), and heroku may want to take account. |
The warning is a bit...premature. It was added to puma on the assumption that the developer wouldn't realize that the thread was also running in each fork. But there are cases where we legitimately want a background thread running in the main worker process. I'm not sure how to square those two things. Basically: There's nothing inherently wrong with having threads running in your worker process, but it might not do what some people think. It's a messaging problem. |
Oh! Thanks for the clarification. OK, it's a false positive. I misunderstood, and thought it was warning that the thread would NOT be running in each fork -- I knew we wanted it to be. But in fact it IS running in each fork, and the puma warning is wrongly assuming it should not be. OK, thanks @schneems , now this explanation is here for anyone else that finds it! |
Actually there was a typo in my response it should have been:
I think the reason this works:
Is that ActiveRecord is semi-fork aware. Basically you cannot fork a connection, it's a file descriptor that is tied to the process. If you change the process, you no longer have the same connection. Also you wouldn't want multiple processes to be reading/writing from the same connection anyway. Back in Rails 4.x days, people used to have to manually call to reconnect after fork, but now days if active record sees that the connection to your database isn't good anymore due to forking it will reconnect. Basically: Booting a connection pool reaper before we fork doesn't buy anyone anything. After fork, the connection won't be valid. So, in that sense, it's a good warning. However, due to Acitve Record's fork-aware connection code, the problem is mitigated (i believe) in the forks. |
Ah, ok, I had it right the first time, doh! OK, if the thread is NOT running in every fork.... Ah, but ActiveRecord will restart the reaper in each fork anyway, so it doesn't matter? Then it is maybe kinda sorta a bug, although one with no negative consequences except the confusing warning maybe. But you say say "Booting a connection pool reaper before we fork doesn't buy anyone anything" -- ok, so why is the connection pool reaper being booted before the fork? Would there be a refactor that would keep it from being booted pre-fork, have it booted on-demand when it's actually needed, and avoid the confusing warning? But still good to know the warning can be safely ignored, because AR should be smart enough to restart the reaper in each fork anyway -- although i'm sort of taking your word for it! |
@schneems I'm not sure if I agree this is safe. There is one reaper per pool, not one reaper per connection: rails/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb Line 402 in 5699122
|
I think this is unlikely to be a problem, because the reaper is almost always sleeping, but there could still be an issue if the reaper is reaping when the process forks. |
@nateberkopec or anyone else, understand AR enough to have an idea of how/if this could be corrected? I actually HAVE looked at the Reaper code before and blogged about it, but not in a while. If the Reaper were created only on first database connection instead of on boot, would that resolve it? Or does first database connection happen before fork anyway, so it would still be created too early and result in a warning? I myself remain not entirely confident that every forked process even gets a Reaper -- how sure are we that the forked processes will notice they don't have a Reaper and start one up? Can anyone identify the code that would be responsible for this? Since the Reaper is only handling pathological/unexpected/error cases, you might not notice if a forked process didn't have one; it wouldn't be there taking care of the error conditions it's supposed to take care of, but someone might have a hard time diagnosing any problems that arose as related to threads and pre-forks and the reaper. This stuff is confusing enough, that I"m not totally sure I trust anyone's idea of what's probably going on without really tracing it in the code, which is tricky. |
I think this should be re-opened. It should be fixed using whatever mechanism is used to close the connection pool before fork. |
Nice thanks. It may just be be auto-closed by the bot again if no activity though. I haven't had much luck figuring out how to intervene in the code here, it's not the simplest part of AR. I think there are two parts:
If someone has some hints or useful orientation into the codebase, even if you don't have time to get a PR over the finish line, leaving them here might help me or someone else get it together. |
The pool isn't closed before fork; the pool is discarded (in the forked process) after fork.
Yup.. and there's one [or more, with multi-DB stuff involved] pool per forked process -> reaper[s] per forked process.
Puma is warning that something could go wrong if the owner of the thread isn't implemented correctly, but provides no way for us to indicate that yes, we do know what we're doing. Alternative suggestions welcome, but right now I really don't see a problem to solve here. |
Ah, I see, thanks, that confirms what @schneems was saying all along. Is there any way to manually shut down the reaper? If so, I guess it would be possible to have puma do so before fork, maybe the puma What with puma being the rails default web server, would it make sense to at least document this in puma and/or generate it into default rails puma configs? Either just to avoid the warning in logs, or arguably even to reduce chance of bugs by tidying things up explicitly. |
Thanks, I never knew how that actually worked. Obviously this all makes sense when you realize the entire pool is discarded, not the connections.
If there was a GVL switch some time during when the reaper was running, I think the Reaper's mutex would be toast, but that doesn't matter because the reaper and it's mutex itself will be marked for GC after fork and never used again (because |
The application could be actively using the pool inside the pre-fork process, though -- either because Puma isn't the only thing running in that main process (in which case you'll presumably get a warning for that/those threads too), or because Puma is itself an adjunct to the main show (which is ultimately the same thing, but implies a different command started us / someone else's code owns the main thread), or just because they accessed the database during app boot [in which case it's likely safe to preemptively disconnect, but you can't be sure that's what's going on]. Active Record is obliged to work in other circumstances too, so it's always required to be resilient to arbitrary forks; I'd argue that introducing extra special-case handling for Puma pre-fork increases the number of code paths leading to that point, and thus increases the chance of bugs in each. (Also, "make sure everyone's web server config explicitly disconnects the DB prior to every fork" is an old-timey superstition that we've worked hard to ensure is reliably unnecessary.) My suggestion would be that Puma only warns if it owns the main thread (I haven't looked at whether it already checks that), and provides a way (e.g. a magic thread-local variable name) for thread owners to flag that they are explicitly fork-safe. |
We don't, although in this case I'm sure that we were.
I'm OK with that and it would be super easy for us to implement. |
@nateberkopec there's an edge case where a mid-reap reaper may have the pool locked, and the post-fork process will itself attempt to m = Mutex.new
t = Thread.new { m.synchronize { puts "grabbed lock"; sleep 10; puts "sleep done" } }
Thread.pass
fork { m.synchronize { puts "instant output" } }
m.synchronize { puts "still delayed" }
t.join |
I wasn't suggesting disconnecting the DB, but just stopping the reaper thread. But your points probably still hold -- if there is something pre-fork that is meant to be able to continue doing AR stuff, a missing reaper thread would be unfortunate. So okay, wow this stuff is complicated, sorry if I've been muddying the waters. Incidentally, @schneems, the heroku docs are still recommending:
Based on this conversation, I'm not sure if this is actually necessary, or "old-timey superstition", but just ignore me if this is still good advice after all. |
I'll open a PR to start the discussion here later today. |
I've created #40399 as a resolution. Once the thread-local is decided, we'll add to Puma. |
Re: rails#37066 (comment) Puma (and some other multi-threaded application servers) may check the `Thread` list before and after forking to look for any threads that may have been running during a call to `fork`. Threads running during a `fork` can leave memory and resources open and potentially cause leaks or deadlocks. However, the Reaper thread in ActiveRecord cannot orphan any resources or memory, and will be cleaned up after forking. Setting this thread local variable allows Puma and other application servers to ignore this thread for the purpose of displaying warnings to users about threads present and running during `fork`.
Re: #37066 (comment) Puma (and some other multi-threaded application servers) may check the `Thread` list before and after forking to look for any threads that may have been running during a call to `fork`. Threads running during a `fork` can leave memory and resources open and potentially cause leaks or deadlocks. However, the Reaper thread in ActiveRecord cannot orphan any resources or memory, and will be cleaned up after forking. Setting this thread local variable allows Puma and other application servers to ignore this thread for the purpose of displaying warnings to users about threads present and running during `fork`. Co-authored-by: Eugene Kenny <elkenny@gmail.com>
This will be resolved once #40399 and puma/puma#2475 are released. |
For Puma, we plan a 5.1 release with this change somtime around the end of Nov/start of Dec. https://github.com/puma/puma/milestone/12 |
Hello from 4 years ago. One pattern that I have developed on buildpacks is to never introduce a warning without a way to disable it via configuration. I don't think we want to completely disable this warning, but it would be nice if app developers and other libraries that know they're running in Puma could tell us "I know what I'm doing, nothing to see here" To that end, maybe we could add an API. Something like
|
@schneems is that not what the above PRs did? |
Yes. I was not paying full attention when I wrote that message. |
start syncing thread on demand, not on config. threads don't survive forking, so on-config runs into trouble with Puma. rails/rails#37066 (comment)
Steps to reproduce
preload_app
andworkers
commands.Expected behavior
Lack of warning, unless it's really dangerous. Is there a way to avoid that warning? If not possibly a documentation should mention that when you enable combination of those options (which config file recommends), you will get that warning
Actual behavior
No idea if this warning is dengerous or not, but probably should not happen with the default configuration.
It seems that connection pool is only creating a new thread in a production environment, does not happen in development.
System configuration
Rails version: 6.0.0pre
Ruby version: 2.6.3-p62
Puma version: 3.12.1
The text was updated successfully, but these errors were encountered: