~evanj/e3

ref: 14b25a41e90d66d710fff17beb5728dbaf017fb6 e3/internal/cache/cache.go -rw-r--r-- 3.9 KiB
14b25a41Evan M Jones Fix(builds): Remove master hook. Didn't work due to ref checkout. 1 year, 7 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
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)
		return nil, err
	}

	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)
}