Skip to content

Commit 534917d

Browse files
authored
Don't remove all mirror repository's releases when mirroring (#28817)
Fix #22066 # Purpose This PR fix the releases will be deleted when mirror repository sync the tags. # The problem In the previous implementation of #19125. All releases record in databases of one mirror repository will be deleted before sync. Ref: https://github.com/go-gitea/gitea/pull/19125/files#diff-2aa04998a791c30e5a02b49a97c07fcd93d50e8b31640ce2ddb1afeebf605d02R481 # The Pros This PR introduced a new method which will load all releases from databases and all tags on git data into memory. And detect which tags needs to be inserted, which tags need to be updated or deleted. Only tags releases(IsTag=true) which are not included in git data will be deleted, only tags which sha1 changed will be updated. So it will not delete any real releases include drafts. # The Cons The drawback is the memory usage will be higher than before if there are many tags on this repository. This PR defined a special release struct to reduce columns loaded from database to memory.
1 parent ba24e0b commit 534917d

File tree

2 files changed

+146
-6
lines changed

2 files changed

+146
-6
lines changed

modules/repository/repo.go

+70-6
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,18 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
508508
return nil
509509
}
510510

511+
// shortRelease to reduce load memory, this struct can replace repo_model.Release
512+
type shortRelease struct {
513+
ID int64
514+
TagName string
515+
Sha1 string
516+
IsTag bool
517+
}
518+
519+
func (shortRelease) TableName() string {
520+
return "release"
521+
}
522+
511523
// pullMirrorReleaseSync is a pull-mirror specific tag<->release table
512524
// synchronization which overwrites all Releases from the repository tags. This
513525
// can be relied on since a pull-mirror is always identical to its
@@ -521,16 +533,20 @@ func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, git
521533
return fmt.Errorf("unable to GetTagInfos in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
522534
}
523535
err = db.WithTx(ctx, func(ctx context.Context) error {
524-
//
525-
// clear out existing releases
526-
//
527-
if _, err := db.DeleteByBean(ctx, &repo_model.Release{RepoID: repo.ID}); err != nil {
528-
return fmt.Errorf("unable to clear releases for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
536+
dbReleases, err := db.Find[shortRelease](ctx, repo_model.FindReleasesOptions{
537+
RepoID: repo.ID,
538+
IncludeDrafts: true,
539+
IncludeTags: true,
540+
})
541+
if err != nil {
542+
return fmt.Errorf("unable to FindReleases in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
529543
}
544+
545+
inserts, deletes, updates := calcSync(tags, dbReleases)
530546
//
531547
// make release set identical to upstream tags
532548
//
533-
for _, tag := range tags {
549+
for _, tag := range inserts {
534550
release := repo_model.Release{
535551
RepoID: repo.ID,
536552
TagName: tag.Name,
@@ -547,6 +563,25 @@ func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, git
547563
return fmt.Errorf("unable insert tag %s for pull-mirror Repo[%d:%s/%s]: %w", tag.Name, repo.ID, repo.OwnerName, repo.Name, err)
548564
}
549565
}
566+
567+
// only delete tags releases
568+
if len(deletes) > 0 {
569+
if _, err := db.GetEngine(ctx).Where("repo_id=?", repo.ID).
570+
In("id", deletes).
571+
Delete(&repo_model.Release{}); err != nil {
572+
return fmt.Errorf("unable to delete tags for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
573+
}
574+
}
575+
576+
for _, tag := range updates {
577+
if _, err := db.GetEngine(ctx).Where("repo_id = ? AND lower_tag_name = ?", repo.ID, strings.ToLower(tag.Name)).
578+
Cols("sha1").
579+
Update(&repo_model.Release{
580+
Sha1: tag.Object.String(),
581+
}); err != nil {
582+
return fmt.Errorf("unable to update tag %s for pull-mirror Repo[%d:%s/%s]: %w", tag.Name, repo.ID, repo.OwnerName, repo.Name, err)
583+
}
584+
}
550585
return nil
551586
})
552587
if err != nil {
@@ -556,3 +591,32 @@ func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, git
556591
log.Trace("pullMirrorReleaseSync: done rebuilding %d releases", numTags)
557592
return nil
558593
}
594+
595+
func calcSync(destTags []*git.Tag, dbTags []*shortRelease) ([]*git.Tag, []int64, []*git.Tag) {
596+
destTagMap := make(map[string]*git.Tag)
597+
for _, tag := range destTags {
598+
destTagMap[tag.Name] = tag
599+
}
600+
dbTagMap := make(map[string]*shortRelease)
601+
for _, rel := range dbTags {
602+
dbTagMap[rel.TagName] = rel
603+
}
604+
605+
inserted := make([]*git.Tag, 0, 10)
606+
updated := make([]*git.Tag, 0, 10)
607+
for _, tag := range destTags {
608+
rel := dbTagMap[tag.Name]
609+
if rel == nil {
610+
inserted = append(inserted, tag)
611+
} else if rel.Sha1 != tag.Object.String() {
612+
updated = append(updated, tag)
613+
}
614+
}
615+
deleted := make([]int64, 0, 10)
616+
for _, tag := range dbTags {
617+
if destTagMap[tag.TagName] == nil && tag.IsTag {
618+
deleted = append(deleted, tag.ID)
619+
}
620+
}
621+
return inserted, deleted, updated
622+
}

modules/repository/repo_test.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package repository
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/modules/git"
10+
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func Test_calcSync(t *testing.T) {
15+
gitTags := []*git.Tag{
16+
/*{
17+
Name: "v0.1.0-beta", //deleted tag
18+
Object: git.MustIDFromString(""),
19+
},
20+
{
21+
Name: "v0.1.1-beta", //deleted tag but release should not be deleted because it's a release
22+
Object: git.MustIDFromString(""),
23+
},
24+
*/
25+
{
26+
Name: "v1.0.0", // keep as before
27+
Object: git.MustIDFromString("1006e6e13c73ad3d9e2d5682ad266b5016523485"),
28+
},
29+
{
30+
Name: "v1.1.0", // retagged with new commit id
31+
Object: git.MustIDFromString("bbdb7df30248e7d4a26a909c8d2598a152e13868"),
32+
},
33+
{
34+
Name: "v1.2.0", // new tag
35+
Object: git.MustIDFromString("a5147145e2f24d89fd6d2a87826384cc1d253267"),
36+
},
37+
}
38+
39+
dbReleases := []*shortRelease{
40+
{
41+
ID: 1,
42+
TagName: "v0.1.0-beta",
43+
Sha1: "244758d7da8dd1d9e0727e8cb7704ed4ba9a17c3",
44+
IsTag: true,
45+
},
46+
{
47+
ID: 2,
48+
TagName: "v0.1.1-beta",
49+
Sha1: "244758d7da8dd1d9e0727e8cb7704ed4ba9a17c3",
50+
IsTag: false,
51+
},
52+
{
53+
ID: 3,
54+
TagName: "v1.0.0",
55+
Sha1: "1006e6e13c73ad3d9e2d5682ad266b5016523485",
56+
},
57+
{
58+
ID: 4,
59+
TagName: "v1.1.0",
60+
Sha1: "53ab18dcecf4152b58328d1f47429510eb414d50",
61+
},
62+
}
63+
64+
inserts, deletes, updates := calcSync(gitTags, dbReleases)
65+
if assert.EqualValues(t, 1, len(inserts), "inserts") {
66+
assert.EqualValues(t, *gitTags[2], *inserts[0], "inserts equal")
67+
}
68+
69+
if assert.EqualValues(t, 1, len(deletes), "deletes") {
70+
assert.EqualValues(t, 1, deletes[0], "deletes equal")
71+
}
72+
73+
if assert.EqualValues(t, 1, len(updates), "updates") {
74+
assert.EqualValues(t, *gitTags[1], *updates[0], "updates equal")
75+
}
76+
}

0 commit comments

Comments
 (0)