~evanj/e3

ref: f07259c77881216ff62e998d3125c07041119356 e3/internal/cache/cache.go -rw-r--r-- 4.0 KiB
f07259c7Evan J Fix(cache.go): Don't error out when item is too large for cache. 1 year, 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package cache

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"git.sr.ht/~evanj/e3/internal/db"
	"github.com/bradfitz/gomemcache/memcache"
)

type Cache struct {
	log     *log.Logger
	baseKey string
	data    dataLayer
	mc      *memcache.Client
}

type dataLayer interface {
	UserNew(ctx context.Context, username, password string) (*db.User, error)
	UserGet(ctx context.Context, username, password string) (*db.User, error)
	UserGetFromToken(ctx context.Context, token string) (*db.User, error)
	DataNew(ctx context.Context, user *db.User, blob []byte, public bool) (*db.Data, error)
	DataGet(ctx context.Context, URL string) (*db.Data, error)
	DataGetWithUser(ctx context.Context, user *db.User, URL string) (*db.Data, error)
	InviteNew(ctx context.Context, user *db.User) (*db.Invite, error)
	InviteValid(ctx context.Context, inviteID string) error
	InviteUse(ctx context.Context, inviteID string) error
}

func New(log *log.Logger, key string, data dataLayer, memcacheServer string) *Cache {
	c := &Cache{
		log,
		key,
		data,
		memcache.New(memcacheServer),
	}
	return c
}

func (c *Cache) UserNew(ctx context.Context, username, password string) (*db.User, error) {
	return c.data.UserNew(ctx, username, password)
}

func (c *Cache) UserGet(ctx context.Context, username, password string) (*db.User, error) {
	return c.data.UserGet(ctx, username, password)
}

func (c *Cache) UserGetFromToken(ctx context.Context, token string) (*db.User, error) {
	return c.data.UserGetFromToken(ctx, token)
}

func (c *Cache) DataNew(ctx context.Context, user *db.User, blob []byte, public bool) (*db.Data, error) {
	data, err := c.data.DataNew(ctx, user, blob, public)
	if err != nil {
		return nil, err
	}

	c.log.Println("attempting to add new item to cache", data.URL)
	return c.toCache(c.key(user.Username, public, data.URL), data)
}

func (c *Cache) DataGet(ctx context.Context, URL string) (*db.Data, error) {
	key := c.key("", true, URL)

	cached, err := c.mc.Get(key)
	if err != nil {
		c.log.Println("not in cache", URL)

		data, err := c.data.DataGet(ctx, URL)
		if err != nil {
			return nil, err
		}

		return c.toCache(key, data)
	}

	c.log.Println("found in cache", URL)
	return c.fromCache(cached.Value)
}

func (c *Cache) DataGetWithUser(ctx context.Context, user *db.User, URL string) (*db.Data, error) {
	key := c.key(user.Username, false, URL)

	cached, err := c.mc.Get(key)
	if err != nil {
		c.log.Println("not in cache", URL)

		data, err := c.data.DataGetWithUser(ctx, user, URL)
		if err != nil {
			return nil, err
		}

		return c.toCache(key, data)
	}

	c.log.Println("found in cache", URL)
	return c.fromCache(cached.Value)
}

func (c *Cache) key(username string, public bool, URL string) string {
	if public {
		return fmt.Sprintf("%s::db::data::%s", c.baseKey, URL)
	}

	return fmt.Sprintf("%s::db::data::%s::%s", c.baseKey, username, URL)
}

func (c *Cache) toCache(key string, data *db.Data) (*db.Data, error) {
	bytes, err := json.Marshal(data)
	if err != nil {
		c.log.Println("failed to marshal item", key, data.URL)
		return nil, err
	}

	if err := c.mc.Add(&memcache.Item{Key: key, Value: bytes}); err != nil {
		c.log.Println("failed to add item to memcache", key, data.URL, err)
		// File is likely too big for current memcached settings.
		// Don't error.
		// return nil, err
		return data, nil
	}

	c.log.Println("successfully added item to memcache", key, data.URL)
	return data, nil
}

func (c *Cache) fromCache(bytes []byte) (*db.Data, error) {
	var data *db.Data
	if err := json.Unmarshal(bytes, &data); err != nil {
		c.log.Println("failed to unmarshal item from cache")
		return nil, err
	}

	c.log.Println("successfully unmarshal'd from cache", data.URL)
	return data, nil
}

func (c *Cache) InviteNew(ctx context.Context, user *db.User) (*db.Invite, error) {
	return c.data.InviteNew(ctx, user)
}

func (c *Cache) InviteValid(ctx context.Context, inviteID string) error {
	return c.data.InviteValid(ctx, inviteID)
}

func (c *Cache) InviteUse(ctx context.Context, inviteID string) error {
	return c.data.InviteUse(ctx, inviteID)
}