tangled
alpha
login
or
join now
whey.party
/
red-dwarf-server
13
fork
atom
collection of golang services under the Red Dwarf umbrella
server.reddwarf.app
bluesky
reddwarf
microcosm
appview
13
fork
atom
overview
issues
pulls
pipelines
labelmerge LRU make it not as bad
whey.party
1 month ago
fb51c446
fb6faf41
+127
-189
5 changed files
expand all
collapse all
unified
split
cmd
labelmerge
handlers.go
main.go
go.mod
go.sum
labelmerge
lru
lru.go
+11
-11
cmd/labelmerge/handlers.go
···
28
28
if len(query.Labelers) > MaxLabelersPerQuery || len(query.Subjects) > MaxSubjectsPerQuery {
29
29
return &appreddwarflabelmerge.QueryLabels_Output{
30
30
Error: []*appreddwarflabelmerge.QueryLabels_Error{
31
31
-
{
31
31
+
{ // todo dont do this we should just throw error and not use the partial errors system
32
32
S: "too_many_labels_or_subjects",
33
33
E: ptrString(fmt.Sprintf("Labelers: %d, Subjects: %d", len(query.Labelers), len(query.Subjects))),
34
34
},
···
65
65
}
66
66
67
67
if result.Error != nil {
68
68
-
for _, msg := range result.Error.LabelerResolutionFailure {
68
68
+
for _, did := range result.Error.LabelerResolutionFailure {
69
69
out.Error = append(out.Error, &appreddwarflabelmerge.QueryLabels_Error{
70
70
-
S: "labeler_resolution_failure",
71
71
-
E: &msg,
70
70
+
S: did,
71
71
+
E: ptrString("labeler_resolution_failure"),
72
72
})
73
73
}
74
74
-
for _, msg := range result.Error.QueryLabelsTooManyPages {
74
74
+
for _, did := range result.Error.QueryLabelsTooManyPages {
75
75
out.Error = append(out.Error, &appreddwarflabelmerge.QueryLabels_Error{
76
76
-
S: "too_many_pages",
77
77
-
E: &msg,
76
76
+
S: did,
77
77
+
E: ptrString("too_many_pages"),
78
78
})
79
79
}
80
80
-
for _, msg := range result.Error.LabelerQueryFailure {
80
80
+
for _, did := range result.Error.LabelerQueryFailure {
81
81
out.Error = append(out.Error, &appreddwarflabelmerge.QueryLabels_Error{
82
82
-
S: "labeler_query_failure",
83
83
-
E: &msg,
82
82
+
S: did,
83
83
+
E: ptrString("labeler_query_failure"),
84
84
})
85
85
}
86
86
}
···
97
97
out := &appreddwarflabelmerge.QueryLabels_Output{
98
98
Error: []*appreddwarflabelmerge.QueryLabels_Error{
99
99
{
100
100
-
S: "timeout",
100
100
+
S: "timeout", // todo dont do this we should explicitly point out the slow labeler and return the succesful labels
101
101
E: &timeoutMsg,
102
102
},
103
103
},
+13
-2
cmd/labelmerge/main.go
···
383
383
if err != nil {
384
384
log.Fatalf("Failed to initialize LRU cache: %v", err)
385
385
}
386
386
-
cache.SetCapacity(1000000)
386
386
+
//cache.SetCapacity(1000000)
387
387
388
388
dir := identity.DefaultDirectory() // Cached with 24hr TTL
389
389
···
778
778
fullURL := baseURL + "?" + params.Encode()
779
779
780
780
// Create HTTP request with timeout
781
781
-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
781
781
+
ctx, cancel := context.WithTimeout(context.Background(), IncomingQueryTimeout)
782
782
defer cancel()
783
783
784
784
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
···
955
955
}
956
956
957
957
func main() {
958
958
+
958
959
log.SetFlags(log.LstdFlags | log.Lshortfile)
959
960
960
961
service := NewLabelResolutionService()
···
963
964
server := &Server{Service: service}
964
965
965
966
e := echo.New()
967
967
+
//pprof.Register(e)
968
968
+
966
969
e.Use(middleware.Recover())
970
970
+
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
971
971
+
AllowOrigins: []string{"*"},
972
972
+
AllowMethods: []string{
973
973
+
http.MethodGet,
974
974
+
http.MethodOptions,
975
975
+
},
976
976
+
AllowHeaders: []string{"*"},
977
977
+
}))
967
978
968
979
// your own routes
969
980
e.GET("/", server.root)
+15
-15
go.mod
···
3
3
go 1.25.4
4
4
5
5
require (
6
6
-
github.com/bluesky-social/indigo v0.0.0-20260128065308-121c189aef50
6
6
+
github.com/bluesky-social/indigo v0.0.0-20260129212913-baa889cd148a
7
7
github.com/ericvolp12/jwt-go-secp256k1 v0.0.2
8
8
github.com/gin-contrib/cors v1.7.6
9
9
github.com/gin-gonic/gin v1.11.0
···
12
12
github.com/gorilla/websocket v1.5.3
13
13
github.com/hashicorp/golang-lru/arc/v2 v2.0.7
14
14
github.com/klauspost/compress v1.18.2
15
15
-
github.com/labstack/echo/v4 v4.11.3
15
15
+
github.com/labstack/echo/v4 v4.13.3
16
16
github.com/prometheus/client_golang v1.23.2
17
17
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b
18
18
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
···
52
52
github.com/jbenet/goprocess v0.1.4 // indirect
53
53
github.com/jinzhu/inflection v1.0.0 // indirect
54
54
github.com/jinzhu/now v1.1.5 // indirect
55
55
-
github.com/labstack/gommon v0.4.1 // indirect
55
55
+
github.com/labstack/gommon v0.4.2 // indirect
56
56
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
57
57
github.com/lestrrat-go/httpcc v1.0.1 // indirect
58
58
github.com/lestrrat-go/httprc v1.0.4 // indirect
59
59
github.com/lestrrat-go/iter v1.0.2 // indirect
60
60
github.com/lestrrat-go/jwx/v2 v2.0.12 // indirect
61
61
github.com/lestrrat-go/option v1.0.1 // indirect
62
62
-
github.com/mattn/go-colorable v0.1.13 // indirect
62
62
+
github.com/mattn/go-colorable v0.1.14 // indirect
63
63
github.com/opentracing/opentracing-go v1.2.0 // indirect
64
64
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect
65
65
github.com/segmentio/asm v1.2.0 // indirect
···
108
108
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
109
109
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
110
110
github.com/prometheus/client_model v0.6.2 // indirect
111
111
-
github.com/prometheus/common v0.66.1 // indirect
112
112
-
github.com/prometheus/procfs v0.16.1 // indirect
111
111
+
github.com/prometheus/common v0.67.5 // indirect
112
112
+
github.com/prometheus/procfs v0.19.2 // indirect
113
113
github.com/quic-go/qpack v0.5.1 // indirect
114
114
github.com/quic-go/quic-go v0.54.0 // indirect
115
115
github.com/spaolacci/murmur3 v1.1.0 // indirect
···
122
122
go.opentelemetry.io/otel/metric v1.38.0 // indirect
123
123
go.opentelemetry.io/otel/trace v1.38.0 // indirect
124
124
go.uber.org/mock v0.5.0 // indirect
125
125
-
go.yaml.in/yaml/v2 v2.4.2 // indirect
125
125
+
go.yaml.in/yaml/v2 v2.4.3 // indirect
126
126
golang.org/x/arch v0.20.0 // indirect
127
127
-
golang.org/x/crypto v0.41.0 // indirect
128
128
-
golang.org/x/mod v0.26.0 // indirect
129
129
-
golang.org/x/net v0.43.0 // indirect
130
130
-
golang.org/x/sync v0.16.0 // indirect
131
131
-
golang.org/x/sys v0.35.0 // indirect
132
132
-
golang.org/x/text v0.28.0 // indirect
133
133
-
golang.org/x/tools v0.35.0 // indirect
127
127
+
golang.org/x/crypto v0.47.0 // indirect
128
128
+
golang.org/x/mod v0.31.0 // indirect
129
129
+
golang.org/x/net v0.49.0 // indirect
130
130
+
golang.org/x/sync v0.19.0 // indirect
131
131
+
golang.org/x/sys v0.40.0 // indirect
132
132
+
golang.org/x/text v0.33.0 // indirect
133
133
+
golang.org/x/tools v0.40.0 // indirect
134
134
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
135
135
-
google.golang.org/protobuf v1.36.9 // indirect
135
135
+
google.golang.org/protobuf v1.36.11 // indirect
136
136
lukechampine.com/blake3 v1.4.1 // indirect
137
137
)
+30
-32
go.sum
···
6
6
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
7
7
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
8
8
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
9
9
-
github.com/bluesky-social/indigo v0.0.0-20260128065308-121c189aef50 h1:rpGmYvqqniy0gn0WJc4tZtlvPRrY5fVEwpV3WgFtdRk=
10
10
-
github.com/bluesky-social/indigo v0.0.0-20260128065308-121c189aef50/go.mod h1:KIy0FgNQacp4uv2Z7xhNkV3qZiUSGuRky97s7Pa4v+o=
9
9
+
github.com/bluesky-social/indigo v0.0.0-20260129212913-baa889cd148a h1:fBniCrEkDIGVW6/6zyl72fnFOIAeeNXUF2U+L3MHaD8=
10
10
+
github.com/bluesky-social/indigo v0.0.0-20260129212913-baa889cd148a/go.mod h1:VG/LeqLGNI3Ew7lsYixajnZGFfWPv144qbUddh+Oyag=
11
11
github.com/bluesky-social/jetstream v0.0.0-20251009222037-7d7efa58d7f1 h1:ovcRKN1iXZnY5WApVg+0Hw2RkwMH0ziA7lSAA8vellU=
12
12
github.com/bluesky-social/jetstream v0.0.0-20251009222037-7d7efa58d7f1/go.mod h1:5PtGi4r/PjEVBBl+0xWuQn4mBEjr9h6xsfDBADS6cHs=
13
13
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
···
190
190
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
191
191
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
192
192
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
193
193
-
github.com/labstack/echo/v4 v4.11.3 h1:Upyu3olaqSHkCjs1EJJwQ3WId8b8b1hxbogyommKktM=
194
194
-
github.com/labstack/echo/v4 v4.11.3/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws=
195
195
-
github.com/labstack/gommon v0.4.1 h1:gqEff0p/hTENGMABzezPoPSRtIh1Cvw0ueMOe0/dfOk=
196
196
-
github.com/labstack/gommon v0.4.1/go.mod h1:TyTrpPqxR5KMk8LKVtLmfMjeQ5FEkBYdxLYPw/WfrOM=
193
193
+
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
194
194
+
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
195
195
+
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
196
196
+
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
197
197
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
198
198
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
199
199
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
···
229
229
github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI=
230
230
github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo=
231
231
github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc=
232
232
-
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
233
233
-
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
232
232
+
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
233
233
+
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
234
234
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
235
235
-
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
236
235
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
237
236
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
238
237
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
···
283
282
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
284
283
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
285
284
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
286
286
-
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
287
287
-
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
288
288
-
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
289
289
-
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
285
285
+
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
286
286
+
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
287
287
+
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
288
288
+
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
290
289
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
291
290
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
292
291
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
···
377
376
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
378
377
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
379
378
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
380
380
-
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
381
381
-
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
379
379
+
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
380
380
+
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
382
381
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
383
382
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
384
383
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
···
387
386
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
388
387
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
389
388
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
390
390
-
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
391
391
-
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
389
389
+
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
390
390
+
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
392
391
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
393
392
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
394
393
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
···
396
395
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
397
396
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
398
397
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
399
399
-
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
400
400
-
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
398
398
+
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
399
399
+
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
401
400
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
402
401
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
403
402
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
···
408
407
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
409
408
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
410
409
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
411
411
-
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
412
412
-
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
410
410
+
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
411
411
+
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
413
412
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
414
413
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
415
414
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
416
415
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
417
416
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
418
417
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
419
419
-
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
420
420
-
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
418
418
+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
419
419
+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
421
420
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
422
421
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
423
422
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
···
429
428
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
430
429
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
431
430
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
432
432
-
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
433
431
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
434
432
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
435
433
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
436
434
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
437
437
-
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
438
438
-
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
435
435
+
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
436
436
+
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
439
437
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
440
438
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
441
439
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
···
447
445
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
448
446
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
449
447
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
450
450
-
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
451
451
-
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
448
448
+
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
449
449
+
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
452
450
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
453
451
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
454
452
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
···
463
461
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
464
462
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
465
463
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
466
466
-
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
467
467
-
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
464
464
+
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
465
465
+
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
468
466
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
469
467
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
470
468
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
471
469
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
472
470
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
473
471
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
474
474
-
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
475
475
-
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
472
472
+
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
473
473
+
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
476
474
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
477
475
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
478
476
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+58
-129
labelmerge/lru/lru.go
···
1
1
package lru
2
2
3
3
import (
4
4
-
"container/list"
5
4
"fmt"
6
5
"sync"
7
6
8
7
badger "github.com/dgraph-io/badger/v4"
9
8
)
10
9
11
11
-
// Cache is a persistent LRU cache implementation
10
10
+
// Cache is a persistent, pure Badger-backed cache
12
11
type Cache[K comparable, V any] struct {
13
12
db *badger.DB
14
14
-
mu sync.RWMutex
15
15
-
16
16
-
// In-memory LRU tracking (Key -> List Element)
17
17
-
cache map[K]*list.Element
18
18
-
list *list.List
13
13
+
mu sync.RWMutex // protects only internal operations, not the DB itself
19
14
20
20
-
// Serialization functions
21
15
serialize func(V) ([]byte, error)
22
16
deserialize func([]byte) (V, error)
23
17
···
26
20
OnRemove func(K)
27
21
}
28
22
29
29
-
// New creates a new persistent LRU cache
30
30
-
func New[K comparable, V any](dbPath string, serialize func(V) ([]byte, error), deserialize func([]byte) (V, error)) (*Cache[K, V], error) {
31
31
-
opts := badger.DefaultOptions(dbPath).WithLogger(nil) // Suppress Badger logs
23
23
+
// New creates a new Badger-backed cache
24
24
+
func New[K comparable, V any](
25
25
+
dbPath string,
26
26
+
serialize func(V) ([]byte, error),
27
27
+
deserialize func([]byte) (V, error),
28
28
+
) (*Cache[K, V], error) {
29
29
+
30
30
+
opts := badger.DefaultOptions(dbPath).WithLogger(nil) // suppress Badger logs
32
31
db, err := badger.Open(opts)
33
32
if err != nil {
34
33
return nil, fmt.Errorf("failed to open Badger DB: %w", err)
35
34
}
36
35
37
37
-
c := &Cache[K, V]{
36
36
+
return &Cache[K, V]{
38
37
db: db,
39
39
-
cache: make(map[K]*list.Element),
40
40
-
list: list.New(),
41
38
serialize: serialize,
42
39
deserialize: deserialize,
43
43
-
// Default NOP hooks
44
44
-
OnAdd: func(K) {},
45
45
-
OnRemove: func(K) {},
46
46
-
}
47
47
-
48
48
-
return c, nil
40
40
+
OnAdd: func(K) {},
41
41
+
OnRemove: func(K) {},
42
42
+
}, nil
49
43
}
50
44
51
51
-
// Close closes the underlying BadgerDB
45
45
+
// Close closes the underlying DB
52
46
func (c *Cache[K, V]) Close() error {
53
47
return c.db.Close()
54
48
}
55
49
56
56
-
// LRUItem is used within the in-memory doubly linked list to track order
57
57
-
type LRUItem[K comparable, V any] struct {
58
58
-
Key K
59
59
-
Value V
60
60
-
}
61
61
-
62
62
-
// Get retrieves a value from the cache
50
50
+
// Get retrieves a value from Badger
63
51
func (c *Cache[K, V]) Get(key K) (V, bool) {
64
64
-
c.mu.Lock()
65
65
-
defer c.mu.Unlock()
66
66
-
67
67
-
// Check in-memory cache first
68
68
-
if ele, ok := c.cache[key]; ok {
69
69
-
// Move to front (mark as recently used)
70
70
-
c.list.MoveToFront(ele)
71
71
-
return ele.Value.(*LRUItem[K, V]).Value, true
72
72
-
}
52
52
+
var zero V
73
53
74
74
-
// Not in memory, check BadgerDB
75
75
-
var zero V
76
54
err := c.db.View(func(txn *badger.Txn) error {
77
55
item, err := txn.Get([]byte(fmt.Sprintf("%v", key)))
78
78
-
if err == badger.ErrKeyNotFound {
79
79
-
return nil
80
80
-
}
81
56
if err != nil {
82
57
return err
83
58
}
···
87
62
if err != nil {
88
63
return err
89
64
}
90
90
-
91
91
-
// Add to LRU list
92
92
-
ele := c.list.PushFront(&LRUItem[K, V]{Key: key, Value: value})
93
93
-
c.cache[key] = ele
94
94
-
c.OnAdd(key)
95
95
-
65
65
+
zero = value
96
66
return nil
97
67
})
98
68
})
99
69
100
100
-
if err != nil {
70
70
+
if err == badger.ErrKeyNotFound {
71
71
+
return zero, false
72
72
+
} else if err != nil {
101
73
return zero, false
102
74
}
103
75
104
104
-
return zero, false
76
76
+
// Trigger hook
77
77
+
if c.OnAdd != nil {
78
78
+
c.OnAdd(key)
79
79
+
}
80
80
+
return zero, true
105
81
}
106
82
107
107
-
// Put stores a value in the cache
83
83
+
// Put stores a value in Badger
108
84
func (c *Cache[K, V]) Put(key K, value V) {
109
109
-
c.mu.Lock()
110
110
-
defer c.mu.Unlock()
111
111
-
112
112
-
// Serialize the value
113
85
serializedValue, err := c.serialize(value)
114
86
if err != nil {
115
87
return
116
88
}
117
89
118
118
-
// Write to BadgerDB
119
90
err = c.db.Update(func(txn *badger.Txn) error {
120
91
return txn.Set([]byte(fmt.Sprintf("%v", key)), serializedValue)
121
92
})
···
123
94
return
124
95
}
125
96
126
126
-
// Update LRU list
127
127
-
if ele, ok := c.cache[key]; ok {
128
128
-
// Update existing value
129
129
-
ele.Value.(*LRUItem[K, V]).Value = value
130
130
-
c.list.MoveToFront(ele)
131
131
-
} else {
132
132
-
// Add new item
133
133
-
ele := c.list.PushFront(&LRUItem[K, V]{Key: key, Value: value})
134
134
-
c.cache[key] = ele
97
97
+
if c.OnAdd != nil {
135
98
c.OnAdd(key)
136
99
}
137
100
}
138
101
139
139
-
// Remove removes a value from the cache
102
102
+
// Remove deletes a value from Badger
140
103
func (c *Cache[K, V]) Remove(key K) {
141
141
-
c.mu.Lock()
142
142
-
defer c.mu.Unlock()
143
143
-
144
144
-
// Remove from BadgerDB
145
145
-
err := c.db.Update(func(txn *badger.Txn) error {
104
104
+
_ = c.db.Update(func(txn *badger.Txn) error {
146
105
return txn.Delete([]byte(fmt.Sprintf("%v", key)))
147
106
})
148
148
-
if err != nil && err != badger.ErrKeyNotFound {
149
149
-
return
150
150
-
}
151
107
152
152
-
// Remove from LRU list
153
153
-
if ele, ok := c.cache[key]; ok {
154
154
-
c.list.Remove(ele)
155
155
-
delete(c.cache, key)
108
108
+
if c.OnRemove != nil {
156
109
c.OnRemove(key)
157
110
}
158
111
}
159
112
160
160
-
// Len returns the number of items in the cache
113
113
+
// Len counts the number of keys in Badger
161
114
func (c *Cache[K, V]) Len() int {
162
162
-
c.mu.RLock()
163
163
-
defer c.mu.RUnlock()
164
164
-
return len(c.cache)
115
115
+
count := 0
116
116
+
_ = c.db.View(func(txn *badger.Txn) error {
117
117
+
iter := txn.NewIterator(badger.DefaultIteratorOptions)
118
118
+
defer iter.Close()
119
119
+
120
120
+
for iter.Rewind(); iter.Valid(); iter.Next() {
121
121
+
count++
122
122
+
}
123
123
+
return nil
124
124
+
})
125
125
+
return count
165
126
}
166
127
167
167
-
// Keys returns all keys in the cache
128
128
+
// Keys returns all keys in Badger
168
129
func (c *Cache[K, V]) Keys() []K {
169
169
-
c.mu.RLock()
170
170
-
defer c.mu.RUnlock()
130
130
+
var keys []K
131
131
+
_ = c.db.View(func(txn *badger.Txn) error {
132
132
+
iter := txn.NewIterator(badger.DefaultIteratorOptions)
133
133
+
defer iter.Close()
171
134
172
172
-
keys := make([]K, 0, len(c.cache))
173
173
-
for k := range c.cache {
174
174
-
keys = append(keys, k)
175
175
-
}
135
135
+
for iter.Rewind(); iter.Valid(); iter.Next() {
136
136
+
item := iter.Item()
137
137
+
k := item.Key()
138
138
+
var key K
139
139
+
fmt.Sscanf(string(k), "%v", &key)
140
140
+
keys = append(keys, key)
141
141
+
}
142
142
+
return nil
143
143
+
})
176
144
return keys
177
145
}
178
146
179
179
-
// Clear removes all items from the cache
147
147
+
// Clear removes all keys
180
148
func (c *Cache[K, V]) Clear() error {
181
181
-
c.mu.Lock()
182
182
-
defer c.mu.Unlock()
183
183
-
184
184
-
// Clear in-memory cache
185
185
-
c.cache = make(map[K]*list.Element)
186
186
-
c.list.Init()
187
187
-
188
188
-
// Clear BadgerDB
189
149
return c.db.DropAll()
190
150
}
191
191
-
192
192
-
// SetCapacity sets the maximum number of items the cache can hold
193
193
-
// When the capacity is exceeded, the least recently used items are evicted
194
194
-
func (c *Cache[K, V]) SetCapacity(capacity int) {
195
195
-
c.mu.Lock()
196
196
-
defer c.mu.Unlock()
197
197
-
198
198
-
for len(c.cache) > capacity {
199
199
-
// Get the least recently used item (back of the list)
200
200
-
ele := c.list.Back()
201
201
-
if ele == nil {
202
202
-
break
203
203
-
}
204
204
-
205
205
-
item := ele.Value.(*LRUItem[K, V])
206
206
-
key := item.Key
207
207
-
208
208
-
// Remove from BadgerDB
209
209
-
err := c.db.Update(func(txn *badger.Txn) error {
210
210
-
return txn.Delete([]byte(fmt.Sprintf("%v", key)))
211
211
-
})
212
212
-
if err != nil && err != badger.ErrKeyNotFound {
213
213
-
continue
214
214
-
}
215
215
-
216
216
-
// Remove from LRU list
217
217
-
c.list.Remove(ele)
218
218
-
delete(c.cache, key)
219
219
-
c.OnRemove(key)
220
220
-
}
221
221
-
}