-
Notifications
You must be signed in to change notification settings - Fork 619
/
usercache.go
141 lines (112 loc) · 3.63 KB
/
usercache.go
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
// Package usercache contains user related CRUD functionality with caching.
package usercache
import (
"context"
"net/mail"
"time"
"github.com/ardanlabs/service/business/domain/userbus"
"github.com/ardanlabs/service/business/sdk/order"
"github.com/ardanlabs/service/business/sdk/page"
"github.com/ardanlabs/service/business/sdk/transaction"
"github.com/ardanlabs/service/foundation/logger"
"github.com/creativecreature/sturdyc"
"github.com/google/uuid"
)
// Store manages the set of APIs for user data and caching.
type Store struct {
log *logger.Logger
storer userbus.Storer
cache *sturdyc.Client[userbus.User]
}
// NewStore constructs the api for data and caching access.
func NewStore(log *logger.Logger, storer userbus.Storer, ttl time.Duration) *Store {
const capacity = 10000
const numShards = 10
const evictionPercentage = 10
return &Store{
log: log,
storer: storer,
cache: sturdyc.New[userbus.User](capacity, numShards, ttl, evictionPercentage),
}
}
// NewWithTx constructs a new Store value replacing the sqlx DB
// value with a sqlx DB value that is currently inside a transaction.
func (s *Store) NewWithTx(tx transaction.CommitRollbacker) (userbus.Storer, error) {
return s.storer.NewWithTx(tx)
}
// Create inserts a new user into the database.
func (s *Store) Create(ctx context.Context, usr userbus.User) error {
if err := s.storer.Create(ctx, usr); err != nil {
return err
}
s.writeCache(usr)
return nil
}
// Update replaces a user document in the database.
func (s *Store) Update(ctx context.Context, usr userbus.User) error {
if err := s.storer.Update(ctx, usr); err != nil {
return err
}
s.writeCache(usr)
return nil
}
// Delete removes a user from the database.
func (s *Store) Delete(ctx context.Context, usr userbus.User) error {
if err := s.storer.Delete(ctx, usr); err != nil {
return err
}
s.deleteCache(usr)
return nil
}
// Query retrieves a list of existing users from the database.
func (s *Store) Query(ctx context.Context, filter userbus.QueryFilter, orderBy order.By, page page.Page) ([]userbus.User, error) {
return s.storer.Query(ctx, filter, orderBy, page)
}
// Count returns the total number of cards in the DB.
func (s *Store) Count(ctx context.Context, filter userbus.QueryFilter) (int, error) {
return s.storer.Count(ctx, filter)
}
// QueryByID gets the specified user from the database.
func (s *Store) QueryByID(ctx context.Context, userID uuid.UUID) (userbus.User, error) {
cachedUsr, ok := s.readCache(userID.String())
if ok {
return cachedUsr, nil
}
usr, err := s.storer.QueryByID(ctx, userID)
if err != nil {
return userbus.User{}, err
}
s.writeCache(usr)
return usr, nil
}
// QueryByEmail gets the specified user from the database by email.
func (s *Store) QueryByEmail(ctx context.Context, email mail.Address) (userbus.User, error) {
cachedUsr, ok := s.readCache(email.Address)
if ok {
return cachedUsr, nil
}
usr, err := s.storer.QueryByEmail(ctx, email)
if err != nil {
return userbus.User{}, err
}
s.writeCache(usr)
return usr, nil
}
// readCache performs a safe search in the cache for the specified key.
func (s *Store) readCache(key string) (userbus.User, bool) {
usr, exists := s.cache.Get(key)
if !exists {
return userbus.User{}, false
}
return usr, true
}
// writeCache performs a safe write to the cache for the specified userbus.
func (s *Store) writeCache(bus userbus.User) {
s.cache.Set(bus.ID.String(), bus)
s.cache.Set(bus.Email.Address, bus)
}
// deleteCache performs a safe removal from the cache for the specified userbus.
func (s *Store) deleteCache(bus userbus.User) {
s.cache.Delete(bus.ID.String())
s.cache.Delete(bus.Email.Address)
}