Uh oh!
There was an error while loading. Please reload this page.
Modify user_ldap checkPassword to not fetch records from ldap each time because of extremely high CPU usage! - #35867
Conversation
come-nc
commented
Jan 2, 2023
Hello, thanks for looking into this!
This means that the new users will not be able to login until cache expires, which may lead to worse user experience on some setups.
Same thing here, forcing attributes update from LDAP on login is most likely to improve user experience and avoid having to wait on background job to see a change applied in Nextcloud. So, I think it was on purpose that cache is not used in checkPassword. That said, it’s of course a big problem if that gets triggered on each request for DAV requests in some cases. We should try to see if we can find a middle ground here, maybe look into cache but if user does not exist, force an LDAP search? @blizzz Thoughts? |
This is fine imo(new users will be able to login) as the function
This is true for sure; my only counter-point is that the cache usually does reset much quicker than the background job run time(especially with lots of users) so the user is fetched from LDAP much more frequently than the bg job(just not everytime). Otherwise, maybe we could even set a config value in
This is already done, please see my point about the
For me its okay to update once on every cache reset but if other users want the update every time, we can use a config option for this as mentioned above. |
come-nc
commented
Jan 3, 2023
No, if the user does not exists server/apps/user_ldap/lib/User_LDAP.php Line 138 in 7ee61dd On the next call if server/apps/user_ldap/lib/User_LDAP.php Lines 121 to 123 in 7ee61dd |
Fair point. My bad, didn't notice the strict comparison against null. I try to see how we can achieve correct behaviour where we fetch if not cached. But also, this seems like an edge case(an important one maybe?) to me. How often can we expect users to try login with a non-existent username and then a new user with that same username is added within the cache timeout time? |
@come-nc What about something like: publicfunctionloginName2UserName($loginName, bool$overrideCacheIfFalse = false) {
$cacheKey = 'loginName2UserName-' . $loginName;
$username = $this->access->connection->getFromCache($cacheKey);
$overrideCache = $overrideCacheIfFalse && $username === false;
if ($username !== null && !$overrideCache) {
return$username;
}
....
}And then: publicfunction checkPassword($uid, $password) {
$username = $this->loginName2UserName($uid, true);
... |
akhil1508
commented
Jan 12, 2023
@come-nc@blizzz@icewind1991 Anything? Please comment if possible if it is okay overall(does not break authentication) so I can test on our servers to see if load goes down a lot by this.. Thanks a lot :) |
come-nc
commented
Jan 12, 2023
Yes it looks good enough that you can test it. |
blizzz
commented
Jan 12, 2023
Mh, Attributes are not processed i.e. not updated anymore now on login despite no cache hit. It will be done only for newly mapped users. Unless I oversaw something? |
server/apps/user_ldap/lib/User_LDAP.php Line 126 in 5c4b4bd Looks like I am mistaken.. Seems like over here, an empty array is sent even after cache invalidation. server/apps/user_ldap/lib/Access.php Line 881 in 5c4b4bd You are right, let me see how this can be handled. |
@blizzz Handled now. Please check. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
@blizzz Seems like I removed reviewers by mistake and am unable to re-add :/ Please do re-add @come-nc and @icewind1991 when possible |
akhil1508
commented
Jan 19, 2023
@come-nc@blizzz@icewind1991 It seems good on test servers. Do you think it's okay to put into production as it is currently as server load is crazy high.. |
0f73108 to
49f3fa8CompareNice PR! Let's see that we get it added to 31 🚀 |
akhil1508
commented
Aug 16, 2024
@sorbaugh Do you need anything from my side to make the merge? |
skjnldsv
commented
Aug 16, 2024
@akhil1508 if possible, could you fix the ldap test failure in |
akhil1508
commented
Aug 16, 2024
@skjnldsv Yes please. It doesn't seem like my changes should break unit tests. Is it maybe related to being up to date with master? Should I merge master into the branch again? |
@akhil1508 could you enable us to make modifications to this branch? If possible avoid merging master into this branch, it's preferable if you rebase instead (to avoid creating a merge commit) :) |
akhil1508
commented
Aug 16, 2024
@skjnldsv Is it OK if I add a new PR from a personal fork? Organization forks don't allow maintainer modifications https://github.com/orgs/community/discussions/5634 |
Uh oh!
There was an error while loading. Please reload this page.
Oh! I didn't know that :) |
b33e884 to
2c85165CompareSigned-off-by: Akhil <akhil@e.email>
ddbe21b to
b1230cdCompareakhil1508
commented
Aug 16, 2024
@skjnldsv Rebased and also attempted to fix the unit test |
skjnldsv
commented
Aug 16, 2024
Amazing! Looks good! I approved the tests, let's see if they pass 🤞 |
|
skjnldsv
commented
Aug 16, 2024
Thank you for your patience and commitment @akhil1508 !! 🎉 🎉 |
akhil1508
commented
Aug 16, 2024
And thanks to you too! :) |

Summary
Currently on each
checkPasswordcall, the functiongetLDAPUserByLoginNameis calledThis calls
fetchListOfUserswhich is defined hereSo on each authentication request:
batchApplyUserAttributesDAV mobile clients(for example DAVx5) don't support token auth or oauth yet. This means that they pass the username and password on every single sync request
We have an NC instance serving around 100 DAV sync requests every second
For every sync request the LDAP logs look like(admin bind + search for user):
Proposed solution here
$user->processAttributeshere seems redundant as it is already done during thegetLDAPUserByLoginNamefunction callUNBINDhappens in the destructor of theConnectionclass so pretty sure it is done only after all these SQL commands are completed, which means the LDAP bind stays open till thenPlease do let me know if there are any pitfalls to this and if the ldap record fetching and processing on every single authentication request happens for an unavoidable reason I am not aware of..
Checklist