1package plc
2
3import (
4 "context"
5 "encoding/json"
6 "github.com/bradfitz/gomemcache/memcache"
7 "go.opentelemetry.io/otel/attribute"
8 "time"
9
10 "github.com/bluesky-social/indigo/did"
11 "go.opentelemetry.io/otel"
12)
13
14type MemcachedDidResolver struct {
15 mcd *memcache.Client
16 res did.Resolver
17 maxAge int32
18}
19
20func NewMemcachedDidResolver(res did.Resolver, maxAge time.Duration, servers []string) *MemcachedDidResolver {
21 expiry := int32(0)
22 if maxAge.Seconds() > (30 * 24 * 60 * 60) {
23 // clamp expiry at 30 days minus a minute for memcached
24 expiry = (30 * 24 * 60 * 60) - 60
25 } else {
26 expiry = int32(maxAge.Seconds())
27 }
28 client := memcache.New(servers...)
29 return &MemcachedDidResolver{
30 mcd: client,
31 res: res,
32 maxAge: expiry,
33 }
34}
35
36func (r *MemcachedDidResolver) FlushCacheFor(didstr string) {
37 r.mcd.Delete(didstr)
38 r.res.FlushCacheFor(didstr)
39}
40
41func (r *MemcachedDidResolver) tryCache(didstr string) (*did.Document, bool) {
42 ob, err := r.mcd.Get(didstr)
43 if (ob == nil) || (err != nil) {
44 return nil, false
45 }
46 var doc did.Document
47 err = json.Unmarshal(ob.Value, &doc)
48 if err != nil {
49 // TODO: log error?
50 return nil, false
51 }
52
53 return &doc, true
54}
55
56func (r *MemcachedDidResolver) putCache(did string, doc *did.Document) {
57 blob, err := json.Marshal(doc)
58 if err != nil {
59 // TODO: log error
60 return
61 }
62 item := memcache.Item{
63 Key: did,
64 Value: blob,
65 Expiration: int32(r.maxAge),
66 }
67 r.mcd.Set(&item)
68}
69
70func (r *MemcachedDidResolver) GetDocument(ctx context.Context, didstr string) (*did.Document, error) {
71 ctx, span := otel.Tracer("cacheResolver").Start(ctx, "getDocument")
72 defer span.End()
73
74 doc, ok := r.tryCache(didstr)
75 if ok {
76 span.SetAttributes(attribute.Bool("cache", true))
77 memcacheHitsTotal.Inc()
78 return doc, nil
79 }
80 memcacheMissesTotal.Inc()
81 span.SetAttributes(attribute.Bool("cache", false))
82
83 doc, err := r.res.GetDocument(ctx, didstr)
84 if err != nil {
85 return nil, err
86 }
87
88 r.putCache(didstr, doc)
89 return doc, nil
90}