Monorepo for Tangled tangled.org
8

Configure Feed

Select the types of activity you want to include in your feed.

appview/repo: don't depend on appview-side cache for repo language stats

KnotMirror will maintain both permanent/ephemeral caches.
AppView still has its own cache for search index.

Signed-off-by: Seongmin Lee <git@boltless.me>

author
Seongmin Lee
committer
Tangled
date (May 19, 2026, 11:38 AM +0300) commit 7171efe3 parent 817fd58d change-id rlrwoplo
+29 -112
+23 -71
appview/repo/index.go
··· 3 3 import ( 4 4 "errors" 5 5 "fmt" 6 - "log/slog" 7 6 "net/http" 8 7 "net/url" 9 8 "slices" ··· 24 23 "tangled.org/core/appview/models" 25 24 "tangled.org/core/appview/pages" 26 25 "tangled.org/core/appview/pages/markup" 27 - "tangled.org/core/orm" 28 26 "tangled.org/core/types" 29 27 30 28 "github.com/go-chi/chi/v5" ··· 116 114 117 115 var languageInfo []types.RepoLanguageDetails 118 116 if !result.IsEmpty { 119 - // TODO: a bit dirty 120 - langCtx, cancel := context.WithTimeout(r.Context(), 1*time.Second) 121 - defer cancel() 122 - languageInfo, err = rp.getLanguageInfo(langCtx, l, f, result.Ref, ref == "") 117 + languageInfo, err = rp.getLanguageInfo(r.Context(), syntax.DID(f.RepoDid), result.Ref) 123 118 if err != nil { 124 119 l.Warn("failed to compute language percentages", "err", err) 125 120 // non-fatal ··· 154 149 155 150 func (rp *Repo) getLanguageInfo( 156 151 ctx context.Context, 157 - l *slog.Logger, 158 - repo *models.Repo, 159 - currentRef string, 160 - isDefaultRef bool, 152 + repoId syntax.DID, 153 + ref string, 161 154 ) ([]types.RepoLanguageDetails, error) { 162 - // first attempt to fetch from db 163 - langs, err := db.GetRepoLanguages( 164 - rp.db, 165 - orm.FilterEq("repo_did", repo.RepoDid), 166 - orm.FilterEq("ref", currentRef), 167 - ) 168 - 169 - if err != nil || langs == nil || containsOtherLanguage(langs) { 170 - langs = nil 171 - 172 - // non-fatal, fetch langs from ks via XRPC 173 - xrpcc := &indigoxrpc.Client{ 174 - Host: rp.config.KnotMirror.Url, 175 - Client: http.DefaultClient, 176 - } 177 - ls, err := tangled.GitTempListLanguages(ctx, xrpcc, currentRef, repo.RepoDid) 178 - if err != nil { 179 - return nil, fmt.Errorf("calling knotmirror git.listLanguages: %w", err) 180 - } 181 - 182 - if ls == nil || ls.Languages == nil { 183 - return nil, nil 184 - } 185 - 186 - for _, lang := range ls.Languages { 187 - langs = append(langs, models.RepoLanguage{ 188 - RepoDid: syntax.DID(repo.RepoDid), 189 - Ref: currentRef, 190 - IsDefaultRef: isDefaultRef, 191 - Language: lang.Name, 192 - Bytes: lang.Size, 193 - }) 194 - } 195 - 196 - tx, err := rp.db.Begin() 197 - if err != nil { 198 - return nil, err 199 - } 200 - defer tx.Rollback() 201 - 202 - // update appview's cache 203 - err = db.UpdateRepoLanguages(tx, syntax.DID(repo.RepoDid), currentRef, langs) 204 - if err != nil { 205 - // non-fatal 206 - l.Error("failed to cache lang results", "err", err) 207 - } 155 + var langs []models.RepoLanguage 156 + // non-fatal, fetch langs from knotmirror via XRPC 157 + xrpcc := &indigoxrpc.Client{ 158 + Host: rp.config.KnotMirror.Url, 159 + Client: http.DefaultClient, 160 + } 161 + ls, err := tangled.GitTempListLanguages(ctx, xrpcc, ref, repoId.String()) 162 + if err != nil { 163 + return nil, fmt.Errorf("calling knotmirror git.listLanguages: %w", err) 164 + } 208 165 209 - err = tx.Commit() 210 - if err != nil { 211 - return nil, err 212 - } 166 + if ls == nil || ls.Languages == nil { 167 + return nil, nil 213 168 } 214 169 215 170 var total int64 216 - for _, l := range langs { 217 - total += l.Bytes 171 + for _, lang := range ls.Languages { 172 + total += lang.Size 173 + langs = append(langs, models.RepoLanguage{ 174 + RepoDid: repoId, 175 + Ref: ref, 176 + Language: lang.Name, 177 + Bytes: lang.Size, 178 + }) 218 179 } 219 180 220 181 var languageStats []types.RepoLanguageDetails ··· 242 203 }) 243 204 244 205 return languageStats, nil 245 - } 246 - 247 - func containsOtherLanguage(langs []models.RepoLanguage) bool { 248 - for _, l := range langs { 249 - if l.Language == enry.OtherLanguage { 250 - return true 251 - } 252 - } 253 - return false 254 206 } 255 207 256 208 // buildIndexResponse creates a RepoIndexResponse by combining multiple xrpc calls in parallel
+6 -41
appview/repo/opengraph.go
··· 3 3 import ( 4 4 "log" 5 5 "net/http" 6 - "sort" 7 6 "time" 8 7 9 - "github.com/go-enry/go-enry/v2" 10 - "tangled.org/core/appview/db" 8 + "github.com/bluesky-social/indigo/atproto/syntax" 11 9 "tangled.org/core/ogre" 12 - "tangled.org/core/orm" 13 - "tangled.org/core/types" 14 10 ) 15 11 12 + const MaxOpengraphLanguageKinds = 4 13 + 16 14 func (rp *Repo) Opengraph(w http.ResponseWriter, r *http.Request) { 17 15 f, err := rp.repoResolver.Resolve(r) 18 16 if err != nil { ··· 24 22 25 23 avatarUrl := rp.pages.AvatarUrl(f.Did, "256") 26 24 27 - var languageStats []types.RepoLanguageDetails 28 - langs, err := db.GetRepoLanguages( 29 - rp.db, 30 - orm.FilterEq("repo_did", f.RepoDid), 31 - orm.FilterEq("is_default_ref", 1), 32 - ) 25 + languageStats, err := rp.getLanguageInfo(r.Context(), syntax.DID(f.RepoDid), "") 33 26 if err != nil { 34 - log.Printf("failed to get language stats from db: %v", err) 35 - } else if len(langs) > 0 { 36 - var total int64 37 - for _, l := range langs { 38 - total += l.Bytes 39 - } 40 - 41 - for _, l := range langs { 42 - percentage := float32(l.Bytes) / float32(total) * 100 43 - color := enry.GetColor(l.Language) 44 - languageStats = append(languageStats, types.RepoLanguageDetails{ 45 - Name: l.Language, 46 - Percentage: percentage, 47 - Color: color, 48 - }) 49 - } 50 - 51 - sort.Slice(languageStats, func(i, j int) bool { 52 - if languageStats[i].Name == enry.OtherLanguage { 53 - return false 54 - } 55 - if languageStats[j].Name == enry.OtherLanguage { 56 - return true 57 - } 58 - if languageStats[i].Percentage != languageStats[j].Percentage { 59 - return languageStats[i].Percentage > languageStats[j].Percentage 60 - } 61 - return languageStats[i].Name < languageStats[j].Name 62 - }) 27 + log.Printf("failed to get language stats from knotmirror: %v", err) 63 28 } 64 29 65 30 ogLanguages := []ogre.LanguageData{} 66 31 for _, lang := range languageStats { 67 - if len(ogLanguages) >= 5 { 32 + if len(ogLanguages) > MaxOpengraphLanguageKinds { 68 33 break 69 34 } 70 35 ogLanguages = append(ogLanguages, ogre.LanguageData{