Skip to content
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

Ensure isolation_level is set. #219

Merged
merged 1 commit into from
Jan 31, 2024
Merged

Ensure isolation_level is set. #219

merged 1 commit into from
Jan 31, 2024

Conversation

ioquatix
Copy link
Member

@ioquatix ioquatix commented Jan 30, 2024

To avoid potential mis-configuration when used with rails, we can use a railtie to set the isolation_level correctly.

See #218 for more context.

I don't like how we are (optionally) coupling falcon to Rails, but I'm not sure there is a good alternative. I considered a separate gem, but people may miss it.

Types of Changes

  • New feature.

Contribution

@ioquatix ioquatix merged commit 112b7a7 into main Jan 31, 2024
42 of 48 checks passed
@ioquatix ioquatix deleted the rails-isolation_level branch January 31, 2024 04:27
@trevorturk
Copy link
Contributor

trevorturk commented Jan 31, 2024

This change makes me a bit nervous. I attempted to enable :fiber in my Rails app and it created a huge number of connections and overwhelmed my database. I may be misunderstanding what's going on here, but I've been collecting some links for further research:

I think the issue is that Rails will check out a connection when needed, but keep the connection open for the duration of the request. So, if you're hanging on a long running HTTP request (as I am) before sending a response, you may have a lot of open connections at once. There appears to be a workaround in surrounding database calls with a with_connection block, and some of the recent Rails work seems like it may improve the situation since a connection might be released earlier, thus mitigating (but not fundamentally solving) the issue, since I believe you'd still effectively have an uncapped number of connections.

The alternative (using :thread) results in only have a single connection per Falcon worker, which is obviously not ideal, but I'm not sure what a better alternative is at this time (setting aside the with_connection workaround.) I was thinking we might need some way to keep the connection pool in the top level fiber? (I think Falcon has a top level fiber that all child process are descended from?)

In any case, I'm happy to discuss further, and apologies if I'm misunderstanding things here, but I wanted to call attention because I think this may be something that a user should make a conscious decision to enable vs being automatic?

@ioquatix
Copy link
Member Author

Thanks for raising your concern, and it's very reasonable.

I believe you can still explicitly override the isolation level IIUC, so you can explicitly opt into per-thread connection handling/isolation in your config. Do you mind confirming this works? i.e. add an explicit config in config/application.rb and then check ActiveSupport::IsolatedExecutionState.isolation_level.

If that turns out to be a valid use case (which it sounds like it is in your case), let's add further documentation to clarify it.

I believe most users will want this behaviour (per-fiber by default), and the next step is to provide a per-query AR connection pool mode. Once we have that, we can probably ALSO add that here, e.g. config.active_record.connection_pool.persist = false or something.

There are several people working on the AR connection pool issues so I believe we will get to a proper solution by Rails 7.2 if we are lucky or 8.0 if the changes are more extensive.

@trevorturk
Copy link
Contributor

trevorturk commented Feb 1, 2024

Thanks for the thoughtful reply.

After giving it some more consideration, I agree isolation_level = :fiber seems like a better default when using Rails with Falcon from what I'm seeing in GitHub discussions etc, so I'm feeling better about this change. I've been watching the activity around per-query AR connection pooling, which is very exciting for the future!

I was able to confirm that you can override the default in config/application.rb if needed, so that's a clear exit ramp for anyone that runs into problems today. I'm still a bit worried that someone may upgrade Falcon without noticing this change, but I'd imagine that won't be a huge number of people given the current landscape, so it should be fine. It seems like more people are running into the inverse issue, thus changing the default makes sense.

As for considering a bit more documentation, I was thinking perhaps something like the following change/addition:

Rails 7.1 introduced the ability to change its internal isolation level from threads (default) to fibers. When you use falcon with Rails, it will automatically set the isolation level to fibers.

...becomes:

Rails 7.1 introduced the ability to change its internal isolation level from threads (default) to fibers. When you use falcon with Rails, it will automatically set the isolation level to fibers. Beware that this change may increase the utilization of shared resources such as Active Record's connection pool. If you'd like to retain the default behavior, add the following to config/application.rb to reset the isolation level to threads: config.active_support.isolation_level = :thread.

Let me know if you'd like me to draft a PR, or feel free to use that as a starting point, or ignore this if you think it's not worth documenting at this point.

Thanks again!

@ioquatix
Copy link
Member Author

ioquatix commented Feb 1, 2024

That looks super, please draft a PR :)

@trevorturk trevorturk mentioned this pull request Feb 1, 2024
1 task
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.

2 participants