π Bulk load share types - #1967
Open
karlitschek wants to merge 1 commit into
Open
karlitschek wants to merge 1 commit into
karlitschek wants to merge 1 commit into
Conversation
karlitschek
requested review from
enjeck and
juliusknorr
and
a lite review from Copilot
and removed request for
enjeck and
silverkszlo
August 6, 2026 13:14
AndyScherzinger
force-pushed
the
perf/noid/bulk-load-share-types
branch
from
September 15, 2026 08:59
d661748 to
dc334af
Compare
Codecov Reportβ All modified and coverable lines are covered by tests. π’ Thoughts on this report? Let us know! |
AndyScherzinger
force-pushed
the
perf/noid/bulk-load-share-types
branch
from
September 15, 2026 09:18
dc334af to
e0bb991
Compare
AndyScherzinger
marked this pull request as draft
September 15, 2026 22:07
AndyScherzinger
force-pushed
the
perf/noid/bulk-load-share-types
branch
2 times, most recently
from
September 15, 2026 22:32
e40a407 to
6acbb6c
Compare
AndyScherzinger
force-pushed
the
perf/noid/bulk-load-share-types
branch
4 times, most recently
from
September 16, 2026 12:24
62b532f to
a23adcd
Compare
There was a problem hiding this comment.
π‘ Changes recommended
Cache eligibility can suppress share types for a shared-in note not covered by its matching folder lookup.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 16/18 changed files
- Comments generated: 3
- Review effort level: Balanced
AndyScherzinger
force-pushed
the
perf/noid/bulk-load-share-types
branch
from
September 16, 2026 12:57
a23adcd to
92b5654
Compare
AndyScherzinger
marked this pull request as ready for review
September 16, 2026 17:13
AndyScherzinger
force-pushed
the
perf/noid/bulk-load-share-types
branch
3 times, most recently
from
September 17, 2026 09:27
e077783 to
d04aa94
Compare
Note::getData() asks NoteUtil::getShareTypes() for the share types of every note it serialises, and that ran one IManager::getSharesBy() query per share type Γ’β¬β eight per note. The web index endpoint loads the whole collection at once (chunkSize is 0 there), so rendering the note list issued eight queries times the number of notes: about 4000 for a user with 500 notes, purely to decide whether to draw the "shared" indicator dot. IManager::getSharesInFolder() answers for every file in a folder in one go, so the cost becomes one call per folder instead of eight per note. NoteUtil::loadShareTypes() preloads a whole tree that way and getShareTypes() reads from that cache, falling back to the old per-file lookup for the single-note endpoints where preloading a tree would cost more than it saves. This mirrors TagService::loadTags(), which already solves the same problem for favorites and is called from the same place. getSharesInFolder() only reports on a folder's direct children Γ’β¬β passing $shallow = false is rejected by the server Γ’β¬β so gatherNoteFiles() now also returns every folder it walked, and loadShareTypes() queries each one. The payload is deliberately unchanged. Shares are filtered against the same eight types the old code asked about and emitted in the same order, so `shareTypes` and `isShared` are identical to before; types the previous code never requested (TYPE_USERGROUP, the per-user half of a group share) stay unreported. A folder whose owner cannot be resolved disables the preload rather than caching an empty result, so a missing owner can never turn a shared note into an unshared-looking one, and the per-file fallback reports no shares instead of dereferencing that missing owner. Coverage is tracked per note rather than per owner. getSharesInFolder() answers only for a folder's direct children, and only for what the user it ran as has shared, so a note is cached only when some lookup actually covered it: same owner, same parent folder. Sharing a folder and a standalone note into the same tree would otherwise let the folder's lookup mark the owner as loaded and answer "not shared" for the note beside it. getSharesInFolder() reports what one user has shared, so the cache only answers for notes owned by a user the preload queried as. A note owned by someone else Γ’β¬β one shared into the notes folder, which mounts inside it Γ’β¬β is looked up per file as before. Without that guard such a note reads as not shared for its recipient, which is a payload change and not a performance one. One getSharesInFolder() call costs about what serialising one note through the eight getSharesBy() calls it replaces costs, so the preload only pays off once a request serialises at least as many notes as the tree has folders. Which notes those are is known to Helper and not to getAll(): a chunked or pruned request returns a fraction of the collection, and the rest are emitted as bare ids that are never asked for their share types. getAll() therefore hands the walked folders back and Helper preloads for the notes it is about to serialise, so a small sync stays on the per-file path instead of walking the whole tree for a handful of notes. Measured on a dev instance with 59 notes in 14 folders and 12 shares, counting MySQL statements for GET /api/v1/notes, payload identical in every case for the owner and for both share recipients: whole collection 464 -> 194 chunkSize=50 403 -> 185 chunkSize=25 226 -> 160 chunkSize=10 121 -> 121 (preload declined) chunkSize=5 86 -> 86 (preload declined) sync of 3 changed 72 -> 72 (preload declined) Also drops the FIXME next to the hardcoded 15 and uses IShare::TYPE_SCIENCEMESH: the constant has existed since Nextcloud 26 and the app now requires 33. Covered by tests/unit/Service/NoteUtilShareTypesTest.php, which asserts that the query count follows the folder count and not the note count, that a preloaded result equals what the per-file path returns, that the fallbacks still work, and that a note shared in beside a folder from the same owner keeps its share types. Assisted-by: Claude Code:claude-opus-5[1m] Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
AndyScherzinger
force-pushed
the
perf/noid/bulk-load-share-types
branch
from
September 17, 2026 09:31
d04aa94 to
0268d69
Compare
AndyScherzinger
changed the base branch from
main
to
test/noid/add-unit-tests
September 17, 2026 16:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note::getData()asksNoteUtil::getShareTypes()for every note it serialises, and that ran onegetSharesBy()query per share type β eight per note β purely to decide whether to draw the "shared" indicator dot.NoteUtil::loadShareTypes()now preloads with onegetSharesInFolder()call per folder andgetShareTypes()reads from that cache, mirroringTagService::loadTags(). Because onlyHelperknows which notes a response will actually contain,getAll()hands the walked folders back andHelperpreloads for the notes it is about to serialise, declining when there are fewer of them than there are folders β so a chunked or incremental sync stays on the per-file path instead of walking the whole tree for a handful of notes. The cache also only answers for notes owned by a user the preload queried as, so a note shared into someone's notes folder still goes through the per-file path and the payload is unchanged for owner and recipient alike.The query counts below are pinned by
tests/unit/Service/NoteUtilShareTypesTest.php, which asserts that the count follows the folder count rather than the note count, that a preloaded result equals what the per-file path returns, and that both fallbacks still work.Measured on a dev instance with 59 notes in 14 folders and 12 shares, counting MySQL statements for
GET /api/v1/notes; the payload was identical in every row, for the owner and for both share recipients.chunkSize=50chunkSize=25chunkSize=10chunkSize=5π€ AI (if applicable)