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

config: fix correctness issues in reading #44230

Merged
merged 1 commit into from
Mar 25, 2024
Merged

Conversation

pulsejet
Copy link
Member

@pulsejet pulsejet commented Mar 15, 2024

Multiple fixes here.

  1. Checking readability is pointless, since fopen is guaranteed to not throw and return false if the file doesn't exist or is not readable (it even logs a warning when the file isn't readable, which is very nice)
  2. Skip even for non-main fails when we can't open it (it'll crash later anyway otherwise) Show the admin a better error message when a config file cannot be opened instead of throwing a TypeError (see config: fix correctness issues in reading #44230 (comment))
  3. Reduce the size of the critical block
  4. Make sure to unlock the file even if something fails
  5. Check opcache on every request. This is low overhead since it only revalidates the timestamp for config.php (even if timestamp validation is disabled in the opcache config) and actually fixes a lot of correctness issues. Some actions such as upgrading an app make it obvious that the opcache may need to be refreshed, but config changes might happen more subtly, and opcache defeats the whole point of locking the file to begin with.

Side note: while Config::writeData does invalidate the opcache anyway, this is not sufficient since

  1. It may be called from the CLI
  2. It only affects a single PHP-FPM instance; other hosts wont be affected

@pulsejet pulsejet added 3. to review Waiting for reviews php Pull requests that update Php code performance 🚀 labels Mar 15, 2024
@pulsejet pulsejet requested review from skjnldsv, a team, ArtificialOwl and nfebe and removed request for a team March 15, 2024 17:34
@pulsejet pulsejet force-pushed the varun/config-read branch from 54b467a to 0b86a50 Compare March 15, 2024 17:35
@pulsejet
Copy link
Member Author

I believe this is low risk and can be backported as a bugfix (there are technically two "bugs": the handle never being unlocked / closed in certain cases, and all the issues caused by the missing opcache invalidation)

@pulsejet pulsejet force-pushed the varun/config-read branch from 0b86a50 to 2759a28 Compare March 15, 2024 17:46
@szaimen szaimen added this to the Nextcloud 30 milestone Mar 15, 2024
Copy link
Contributor

@emoral435 emoral435 left a comment

Choose a reason for hiding this comment

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

Overall, I like these changes a lot actually. No need to check if a file is readable or not if all we are doing is locking it :) just a small question regarding the sanitization that was removed for the configFilePath

EDIT: it does need to be removed after reading the explanation below!

lib/private/Config.php Show resolved Hide resolved
@skjnldsv skjnldsv requested a review from nickvergessen March 16, 2024 12:24
@come-nc
Copy link
Contributor

come-nc commented Mar 18, 2024

What bugs are you trying to fix? Are those bugs that you encountered yourself?

I’d expect PHP to release all locks on crash, I’m surprised the finally block is needed. Otherwise we’d need this kind of thing in a lot of places.
Also you are silencing the fopen errors, which will make understanding the situation harder in cases where fopen fails.

@pulsejet
Copy link
Member Author

pulsejet commented Mar 18, 2024

What bugs are you trying to fix? Are those bugs that you encountered yourself?

Opcache. For example pulsejet/memories#578 (comment)
I've lost count how many times something similar has been reported

I'm aware the server tuning docs mention the pitfalls of disabling revalidation, but revalidating one file (config.php) and every file on every request aren't the same thing (not even close). By revalidating just the config file for every request (which carries almost no cost), disabling general revalidation becomes much more realistic.

I’d expect PHP to release all locks on crash, I’m surprised the finally block is needed. Otherwise we’d need this kind of thing in a lot of places.

Correct, it's not strictly needed but this is cleaner / more resilient. In case the exception is caught, we no longer rely on PHP GC behavior the request to end for the file to unlock.

Also you are silencing the fopen errors, which will make understanding the situation harder in cases where fopen fails.

Errors like? There were checks on existence and readability so this would've never logged anything either way (this one is a performance fix to save a couple of kernel calls)

@pulsejet
Copy link
Member Author

Also note, the locked/critical block should always be as small as possible (generally speaking), and the shortest way to do it is this.

@come-nc
Copy link
Contributor

come-nc commented Mar 18, 2024

What bugs are you trying to fix? Are those bugs that you encountered yourself?

Opcache. For example pulsejet/memories#578 (comment) I've lost count how many times something similar has been reported

I'm aware the server tuning docs mention the pitfalls of disabling revalidation, but revalidating one file (config.php) and every file on every request aren't the same thing (not even close). By revalidating just the config file for every request (which carries almost no cost), disabling general revalidation becomes much more realistic.

We should not impact everyone to handle the case of a badly configured opcache. If opcache is used without revalidation it must be manually cleaned on app install/update, no? Or maybe it can be done automatically, don’t know. But disabling opcache for everyone on config file looks overkill.

I’d expect PHP to release all locks on crash, I’m surprised the finally block is needed. Otherwise we’d need this kind of thing in a lot of places.

Correct, it's not strictly needed but this is cleaner / more resilient. In case the exception is caught, we no longer rely on PHP GC behavior the request to end for the file to unlock.

Good point, did not think of the case it’s catched.

Also you are silencing the fopen errors, which will make understanding the situation harder in cases where fopen fails.

Errors like? There were checks on existence and readability so this would've never logged anything either way (this one is a performance fix to save a couple of kernel calls)

Any fopen failure other than non-existing file.
https://github.com/php/php-src/blob/master/main/fopen_wrappers.c#L700 too long path for instance.

@pulsejet
Copy link
Member Author

pulsejet commented Mar 18, 2024

We should not impact everyone to handle the case of a badly configured opcache. If opcache is used without revalidation it must be manually cleaned on app install/update, no? Or maybe it can be done automatically, don’t know.

I wouldn't say it's badly configured, this is actually good for performance (even the server tuning doc makes a passing mention). The problem isn't with app installation; opcache is reset when upgrading anyway. But the config file changing is what is the problem; this can happen more often and give rise to very subtle bugs.

But disabling opcache for everyone on config file looks overkill.

Opcache is not disabled, even on the config file. Just the timestamp is revalidated on every request, which is very minimal overhead (notice $force=false in the invalidate). Notably, checking the timestamp is actually much faster than opening and locking the file; things that we already do anyway. While the cost of this is negligible, the correctness guarantee it provides is pretty big.

Here's another way of looking at it: if we consider turning off revalidation as "bad", then the total cost paid here is basically nothing since the timestamp was validated anyway. On the other hand, just validating the config file makes turning off revalidation generally a "valid" opcache configuration (which we actually expect to work well), and reduce the overall overhead.

Any fopen failure other than non-existing file.

Point taken, though I wouldn't consider this a big deal. The check for a valid config file is needed only once (ever), but we pay the cost for every single request, which seems very excessive.

Anyway, can revert this one if you prefer.

@pulsejet
Copy link
Member Author

More thoughts?

Copy link
Contributor

@come-nc come-nc left a comment

Choose a reason for hiding this comment

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

Can you detail the rationale on "Skip even for non-main fails when we can't open it (it'll crash later anyway otherwise)" ?
With this PR the admin will have a hard time noticing the permissions on an additional config file are wrong, nothing is logged or shown.

lib/private/Config.php Outdated Show resolved Hide resolved
@pulsejet pulsejet force-pushed the varun/config-read branch from 2759a28 to 817ab23 Compare March 21, 2024 17:26
@pulsejet
Copy link
Member Author

pulsejet commented Mar 21, 2024

Can you detail the rationale on "Skip even for non-main fails when we can't open it (it'll crash later anyway otherwise)" ? With this PR the admin will have a hard time noticing the permissions on an additional config file are wrong, nothing is logged or shown.

True ... the current behavior (before this patch) is odd; it just goes on with a false in the file and throws a TypeError. Looking at the error you can't get any idea what is wrong either.

I've updated the patch to show a better error. (note we can't actually throw here since the exception gets caught and the config not being loaded leads to more issues)

Before After
image image

Also updated PR description

@pulsejet pulsejet requested a review from come-nc March 21, 2024 18:39
@skjnldsv skjnldsv removed their request for review March 21, 2024 19:21
lib/private/Config.php Show resolved Hide resolved
@pulsejet
Copy link
Member Author

I can't merge due to CI failure, seems unrelated

@emoral435
Copy link
Contributor

Rerunning CI

Signed-off-by: Varun Patil <varunpatil@ucla.edu>
@pulsejet pulsejet force-pushed the varun/config-read branch from 817ab23 to 1585587 Compare March 25, 2024 19:07
@pulsejet pulsejet merged commit 024f689 into master Mar 25, 2024
167 checks passed
@pulsejet pulsejet deleted the varun/config-read branch March 25, 2024 21:12
@Altahrim Altahrim mentioned this pull request Mar 26, 2024
@pulsejet
Copy link
Member Author

pulsejet commented Apr 2, 2024

/backport to stable28

@szaimen
Copy link
Contributor

szaimen commented Apr 2, 2024

/backport to stable29

Copy link

backportbot bot commented Apr 2, 2024

The backport to stable29 failed. Please do this backport manually.

# Switch to the target branch and update it
git checkout stable29
git pull origin stable29

# Create the new backport branch
git checkout -b backport/44230/stable29

# Cherry pick the change from the commit sha1 of the change against the default branch
# This might cause conflicts, resolve them
git cherry-pick 15855876

# Push the cherry pick commit to the remote repository and open a pull request
git push origin backport/44230/stable29

Error: No changes found in backport branch


Learn more about backports at https://docs.nextcloud.com/server/stable/go.php?to=developer-backports.

@pulsejet
Copy link
Member Author

pulsejet commented Apr 2, 2024

/backport to stable29

@szaimen this is already merged to 29

@szaimen
Copy link
Contributor

szaimen commented Apr 2, 2024

Ah I see

joshtrichards added a commit that referenced this pull request Aug 28, 2024
Make changes recently added via #44230 match #8188 to avoid failures in restricted hosting environments.

Fixes #47562

Signed-off-by: Josh <josh.t.richards@gmail.com>
backportbot bot pushed a commit that referenced this pull request Aug 30, 2024
Make changes recently added via #44230 match #8188 to avoid failures in restricted hosting environments.

Fixes #47562

Signed-off-by: Josh <josh.t.richards@gmail.com>
backportbot bot pushed a commit that referenced this pull request Aug 30, 2024
Make changes recently added via #44230 match #8188 to avoid failures in restricted hosting environments.

Fixes #47562

Signed-off-by: Josh <josh.t.richards@gmail.com>
backportbot bot pushed a commit that referenced this pull request Aug 30, 2024
Make changes recently added via #44230 match #8188 to avoid failures in restricted hosting environments.

Fixes #47562

Signed-off-by: Josh <josh.t.richards@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3. to review Waiting for reviews backport-request performance 🚀 php Pull requests that update Php code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants