Monorepo for Tangled tangled.org
2

Configure Feed

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

knotmirror: add redis cache to listLanguages

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

author
Seongmin Lee
committer
Tangled
date (May 12, 2026, 11:47 AM +0300) commit 9acac4bf parent e120048b change-id komotxyr
+45 -2
+1
knotmirror/config/config.go
··· 11 11 PlcUrl string `env:"MIRROR_PLC_URL, default=https://plc.directory"` 12 12 TapUrl string `env:"MIRROR_TAP_URL, default=http://localhost:2480"` 13 13 DbUrl string `env:"MIRROR_DB_URL, required"` 14 + RedisAddr string `env:"MIRROR_REDIS_ADDR, required"` 14 15 KnotUseSSL bool `env:"MIRROR_KNOT_USE_SSL, default=false"` // use SSL for Knot when not scheme is not specified 15 16 KnotSSRF bool `env:"MIRROR_KNOT_SSRF, default=false"` 16 17 GitRepoBasePath string `env:"MIRROR_GIT_BASEPATH, default=repos"`
+4 -1
knotmirror/knotmirror.go
··· 8 8 "time" 9 9 10 10 "github.com/go-chi/chi/v5" 11 + "github.com/redis/go-redis/v9" 11 12 "github.com/prometheus/client_golang/prometheus/promhttp" 12 13 "tangled.org/core/idresolver" 13 14 "tangled.org/core/knotmirror/config" ··· 29 30 if err != nil { 30 31 return fmt.Errorf("initializing db: %w", err) 31 32 } 33 + 34 + rdb := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr}) 32 35 33 36 resolver := idresolver.DefaultResolver(cfg.PlcUrl) 34 37 ··· 53 56 crawler := NewCrawler(logger, db) 54 57 resyncer := NewResyncer(logger, db, gitm, cfg) 55 58 adminpage := NewAdminServer(logger, db, resyncer) 56 - xrpc := xrpc.New(logger, cfg, db, resolver, knotstream) 59 + xrpc := xrpc.New(logger, cfg, db, rdb, resolver, knotstream) 57 60 58 61 // maintain repository list with tap 59 62 // NOTE: this can be removed once we introduce did-for-repo because then we can just listen to KnotStream for #identity events.
+30
knotmirror/xrpc/git_list_languages.go
··· 2 2 3 3 import ( 4 4 "context" 5 + "encoding/json" 5 6 "fmt" 6 7 "math" 7 8 "net/http" ··· 13 14 "tangled.org/core/knotserver/git" 14 15 ) 15 16 17 + const ( 18 + RepoLanguagesByDid = "git_list_languages:repo:%s:%s" 19 + RepoLanguagesTTL = 24 * time.Hour 20 + ) 21 + 16 22 func (x *Xrpc) ListLanguages(w http.ResponseWriter, r *http.Request) { 17 23 var ( 18 24 repoQuery = r.URL.Query().Get("repo") ··· 27 33 return 28 34 } 29 35 36 + if val, err := x.rdb.Get(r.Context(), fmt.Sprintf(RepoLanguagesByDid, repo, ref)).Result(); err == nil { 37 + l.Debug("served from cache") 38 + var langs []*tangled.GitTempListLanguages_Language 39 + err = json.Unmarshal([]byte(val), &langs) 40 + if err == nil { 41 + writeJson(w, http.StatusOK, &tangled.GitTempListLanguages_Output{ 42 + Ref: ref, 43 + Languages: langs, 44 + }) 45 + return 46 + } 47 + } 48 + 30 49 out, err := x.listLanguages(r.Context(), repo, ref) 31 50 if err != nil { 32 51 l.Warn("local mirror failed, trying proxy", "err", err) ··· 58 77 if err != nil { 59 78 return nil, fmt.Errorf("analyzing languages: %w", err) 60 79 } 80 + 81 + langs := sizesToLanguages(sizes) 82 + 83 + go func() { 84 + ctx := context.Background() 85 + encoded, err := json.Marshal(langs) 86 + if err != nil { 87 + return 88 + } 89 + x.rdb.Set(ctx, fmt.Sprintf(RepoLanguagesByDid, repo, ref), encoded, RepoLanguagesTTL) 90 + }() 61 91 62 92 return &tangled.GitTempListLanguages_Output{ 63 93 Ref: ref,
+4 -1
knotmirror/xrpc/xrpc.go
··· 10 10 11 11 "github.com/bluesky-social/indigo/atproto/atclient" 12 12 "github.com/go-chi/chi/v5" 13 + "github.com/redis/go-redis/v9" 13 14 "tangled.org/core/api/tangled" 14 15 "tangled.org/core/idresolver" 15 16 "tangled.org/core/knotmirror/config" ··· 20 21 type Xrpc struct { 21 22 cfg *config.Config 22 23 db *sql.DB 24 + rdb *redis.Client 23 25 resolver *idresolver.Resolver 24 26 ks *knotstream.KnotStream 25 27 logger *slog.Logger 26 28 httpClient *http.Client 27 29 } 28 30 29 - func New(logger *slog.Logger, cfg *config.Config, db *sql.DB, resolver *idresolver.Resolver, ks *knotstream.KnotStream) *Xrpc { 31 + func New(logger *slog.Logger, cfg *config.Config, db *sql.DB, rdb *redis.Client, resolver *idresolver.Resolver, ks *knotstream.KnotStream) *Xrpc { 30 32 return &Xrpc{ 31 33 cfg: cfg, 32 34 db: db, 35 + rdb: rdb, 33 36 resolver: resolver, 34 37 ks: ks, 35 38 logger: log.SubLogger(logger, "xrpc"),
+6
nix/modules/knotmirror.nix
··· 98 98 cfg.package 99 99 ]; 100 100 101 + services.redis.servers.knotmirror = { 102 + enable = true; 103 + port = 6377; 104 + }; 105 + 101 106 systemd.services.tap-knotmirror = { 102 107 description = "knotmirror tap service"; 103 108 after = ["network.target"]; ··· 139 144 "MIRROR_HOSTNAME=${cfg.hostname}" 140 145 "MIRROR_TAP_URL=http://localhost:${toString cfg.tap.port}" 141 146 "MIRROR_DB_URL=${cfg.dbUrl}" 147 + "MIRROR_REDIS_ADDR=localhost:6377" 142 148 "MIRROR_GIT_BASEPATH=/var/lib/knotmirror/repos" 143 149 "MIRROR_KNOT_USE_SSL=${boolToString cfg.knotUseSSL}" 144 150 "MIRROR_KNOT_SSRF=${boolToString cfg.knotSSRF}"