perf: optimize converting releases to feed items (#7221)

- `releasesToFeedItems` is called to convert release structs to feed items, which is then used to render RSS or Atom feeds.
- Optimize the loading of attributes for the releases, introduce `ReleaseList` type which uses caching to load repository and publishers. It also no longer loads release attachments and downloads counts as that is not used in feed items.
- Optimize the composing of meta by introducing caching, this operation is especially slow when the owner is an organization.
- Add unit test (ensures new `LoadAttributes` works correctly).
- Add integration test (ensures that feed output is still as expected).

Loading https://codeberg.org/forgejo/forgejo/releases.rss reduced from ~15s to ~1s. (It is currently is deployed on codeberg.org)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7221
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
This commit is contained in:
Gusted 2025-03-17 09:00:34 +00:00 committed by Earl Warren
parent ccd87001c8
commit d5c8091e08
4 changed files with 192 additions and 8 deletions

View file

@ -298,14 +298,14 @@ func GetFeedType(name string, req *http.Request) (bool, string, string) {
return false, name, ""
}
// feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (items []*feeds.Item, err error) {
for _, rel := range releases {
err := rel.LoadAttributes(ctx)
if err != nil {
return nil, err
}
// feedActionsToFeedItems convert repository releases into feed items.
func releasesToFeedItems(ctx *context.Context, releases repo_model.ReleaseList) (items []*feeds.Item, err error) {
if err := releases.LoadAttributes(ctx); err != nil {
return nil, err
}
composeCache := make(map[int64]map[string]string)
for _, rel := range releases {
var title string
var content template.HTML
@ -315,13 +315,19 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (
title = rel.Title
}
metas, ok := composeCache[rel.RepoID]
if !ok {
metas = rel.Repo.ComposeMetas(ctx)
composeCache[rel.RepoID] = metas
}
link := &feeds.Link{Href: rel.HTMLURL()}
content, err = markdown.RenderString(&markup.RenderContext{
Ctx: ctx,
Links: markup.Links{
Base: rel.Repo.Link(),
},
Metas: rel.Repo.ComposeMetas(ctx),
Metas: metas,
}, rel.Note)
if err != nil {
return nil, err