Skip to content

Commit cd7fc4a

Browse files
committed
feat: cache client. add create tenant example
1 parent e3b320a commit cd7fc4a

11 files changed

+367
-266
lines changed

common/cache.go

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package common
2+
3+
import (
4+
"container/list"
5+
"fmt"
6+
"sync"
7+
)
8+
9+
// Cache is used a LRU (Least recently used) cache replacement policy. adapted from https://github.com/Code-Hex/go-generics-cache/blob/main/policy/lru/lru.go
10+
//
11+
// Discards the least recently used items first. This algorithm requires
12+
// keeping track of what was used when, which is expensive if one wants
13+
// to make sure the algorithm always discards the least recently used item.
14+
type Cache[K comparable, V any] struct {
15+
cap int
16+
list *list.List
17+
items map[K]*list.Element
18+
mu sync.Mutex
19+
}
20+
21+
type entry[K comparable, V any] struct {
22+
key K
23+
val V
24+
}
25+
26+
// Option is an option for LRU cache.
27+
type Option func(*options)
28+
29+
type options struct {
30+
capacity int
31+
}
32+
33+
func newOptions() *options {
34+
return &options{
35+
capacity: 128,
36+
}
37+
}
38+
39+
// WithCapacity is an option to set cache capacity.
40+
func WithCapacity(cap int) Option {
41+
return func(o *options) {
42+
o.capacity = cap
43+
}
44+
}
45+
46+
// NewCache creates a new thread safe LRU cache whose capacity is the default size (128).
47+
func NewCache[K comparable, V any](opts ...Option) *Cache[K, V] {
48+
o := newOptions()
49+
for _, optFunc := range opts {
50+
optFunc(o)
51+
}
52+
return &Cache[K, V]{
53+
cap: o.capacity,
54+
list: list.New(),
55+
items: make(map[K]*list.Element, o.capacity),
56+
}
57+
}
58+
59+
// Get looks up a key's value from the cache.
60+
func (c *Cache[K, V]) Get(key K) (zero V, _ bool) {
61+
c.mu.Lock()
62+
defer c.mu.Unlock()
63+
return c.get(key)
64+
}
65+
66+
func (c *Cache[K, V]) get(key K) (zero V, _ bool) {
67+
e, ok := c.items[key]
68+
if !ok {
69+
return
70+
}
71+
// updates cache order
72+
c.list.MoveToFront(e)
73+
return e.Value.(*entry[K, V]).val, true
74+
}
75+
76+
func (c *Cache[K, V]) Set(key K, val V) {
77+
c.mu.Lock()
78+
defer c.mu.Unlock()
79+
c.set(key, val)
80+
}
81+
82+
// Set sets a value to the cache with key. replacing any existing value.
83+
func (c *Cache[K, V]) set(key K, val V) {
84+
85+
if e, ok := c.items[key]; ok {
86+
// updates cache order
87+
c.list.MoveToFront(e)
88+
entry := e.Value.(*entry[K, V])
89+
entry.val = val
90+
return
91+
}
92+
93+
newEntry := &entry[K, V]{
94+
key: key,
95+
val: val,
96+
}
97+
e := c.list.PushFront(newEntry)
98+
c.items[key] = e
99+
100+
if c.list.Len() > c.cap {
101+
c.deleteOldest()
102+
}
103+
}
104+
105+
// GetOrSet combine Get and Set
106+
func (c *Cache[K, V]) GetOrSet(key K, factory func() (V, error)) (zero V, set bool, err error) {
107+
c.mu.Lock()
108+
defer c.mu.Unlock()
109+
if v, ok := c.get(key); ok {
110+
return v, false, nil
111+
}
112+
//use factory
113+
v, err := factory()
114+
if err != nil {
115+
return zero, false, err
116+
}
117+
c.set(key, v)
118+
return v, true, nil
119+
}
120+
121+
// Keys returns the keys of the cache. the order is from oldest to newest.
122+
func (c *Cache[K, V]) Keys() []K {
123+
c.mu.Lock()
124+
defer c.mu.Unlock()
125+
return c.keys()
126+
}
127+
128+
func (c *Cache[K, V]) keys() []K {
129+
keys := make([]K, 0, len(c.items))
130+
for ent := c.list.Back(); ent != nil; ent = ent.Prev() {
131+
entry := ent.Value.(*entry[K, V])
132+
keys = append(keys, entry.key)
133+
}
134+
return keys
135+
}
136+
137+
// Len returns the number of items in the cache.
138+
func (c *Cache[K, V]) Len() int {
139+
c.mu.Lock()
140+
defer c.mu.Unlock()
141+
return c.list.Len()
142+
}
143+
144+
// Delete deletes the item with provided key from the cache.
145+
func (c *Cache[K, V]) Delete(key K) {
146+
c.mu.Lock()
147+
defer c.mu.Unlock()
148+
c.deleteKey(key)
149+
}
150+
151+
func (c *Cache[K, V]) deleteKey(key K) error {
152+
if e, ok := c.items[key]; ok {
153+
return c.delete(e)
154+
}
155+
return nil
156+
}
157+
158+
// Flush delete all items
159+
func (c *Cache[K, V]) Flush() error {
160+
c.mu.Lock()
161+
defer c.mu.Unlock()
162+
var err error
163+
for _, k := range c.keys() {
164+
nerr := c.deleteKey(k)
165+
if nerr != nil {
166+
if err == nil {
167+
err = nerr
168+
} else {
169+
err = fmt.Errorf("%w; ", err)
170+
}
171+
}
172+
}
173+
return err
174+
}
175+
176+
func (c *Cache[K, V]) deleteOldest() {
177+
c.mu.Lock()
178+
defer c.mu.Unlock()
179+
e := c.list.Back()
180+
c.delete(e)
181+
}
182+
183+
type closable interface {
184+
Close() error
185+
}
186+
187+
func (c *Cache[K, V]) delete(e *list.Element) error {
188+
c.list.Remove(e)
189+
entry := e.Value.(*entry[K, V])
190+
delete(c.items, entry.key)
191+
192+
if f, ok := e.Value.(closable); ok {
193+
return f.Close()
194+
}
195+
return nil
196+
}

common/client_provider.go

+1-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package common
22

33
import (
44
"context"
5-
"database/sql"
65
"github.com/goxiaoy/go-saas/data"
76
)
87

@@ -12,6 +11,7 @@ type (
1211
Get(ctx context.Context, dsn string) (TClient, error)
1312
}
1413

14+
// ClientProviderFunc see ClientProvider
1515
ClientProviderFunc[TClient interface{}] func(ctx context.Context, dsn string) (TClient, error)
1616

1717
//DbProvider resolve TClient from user friendly key
@@ -51,12 +51,3 @@ func (d *DefaultDbProvider[TClient]) Get(ctx context.Context, key string) TClien
5151
}
5252
return c
5353
}
54-
55-
type SqlDbProvider DbProvider[*sql.DB]
56-
57-
var SqlClientProvider ClientProvider[*sql.DB]
58-
59-
// NewSqlDbProvider create db provider which directly use sql.Db
60-
func NewSqlDbProvider(cs data.ConnStrResolver, cp DbOpener) SqlDbProvider {
61-
return NewDbProvider(cs, SqlClientProvider(cp))
62-
}

common/db_name_generator.go

+12-41
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,27 @@ package common
22

33
import (
44
"context"
5-
"github.com/goxiaoy/go-saas/data"
5+
"fmt"
66
)
77

8-
// DbNameGenerator generate table name for tenant. useful for tenant creation
9-
type DbNameGenerator interface {
8+
// ConnStrGenerator generate connection string for tenant. useful for tenant creation
9+
type ConnStrGenerator interface {
1010
Gen(ctx context.Context, tenant TenantInfo) (string, error)
1111
}
1212

13-
// DefaultDbNameGenerator generate only database name for tenant
14-
type DefaultDbNameGenerator struct {
15-
prefix string
16-
postfix string
13+
type DefaultConnStrGenerator struct {
14+
format string
1715
}
1816

19-
var _ DbNameGenerator = (*DefaultDbNameGenerator)(nil)
17+
var _ ConnStrGenerator = (*DefaultConnStrGenerator)(nil)
2018

21-
func NewDbNameGenerator(prefix string, postfix string) *DefaultDbNameGenerator {
22-
return &DefaultDbNameGenerator{prefix: prefix, postfix: postfix}
19+
func NewConnStrGenerator(format string) *DefaultConnStrGenerator {
20+
return &DefaultConnStrGenerator{format: format}
2321
}
2422

25-
func (d *DefaultDbNameGenerator) Gen(ctx context.Context, tenant TenantInfo) (string, error) {
26-
return d.prefix + tenant.GetId() + d.postfix, nil
27-
}
28-
29-
func NewSqliteFileDbNameGenerator(parent string) *DefaultDbNameGenerator {
30-
return NewDbNameGenerator(parent, ".db")
31-
}
32-
33-
// MultiDbNameGenerator generate data.ConnStrings for tenant
34-
type MultiDbNameGenerator struct {
35-
//conf host connection string
36-
conf data.ConnStrings
37-
g DbNameGenerator
38-
}
39-
40-
func NewMultipleDbNameGenerator(conf data.ConnStrings, g DbNameGenerator) *MultiDbNameGenerator {
41-
return &MultiDbNameGenerator{conf: conf, g: g}
42-
}
43-
44-
func (m *MultiDbNameGenerator) GenMulti(ctx context.Context, tenant TenantInfo) (data.ConnStrings, error) {
45-
ret := data.ConnStrings{}
46-
name, err := m.g.Gen(ctx, tenant)
47-
if err != nil {
48-
return nil, err
49-
}
50-
if m.conf != nil {
51-
for k, _ := range m.conf {
52-
ret[k] = name
53-
}
23+
func (d *DefaultConnStrGenerator) Gen(ctx context.Context, tenant TenantInfo) (string, error) {
24+
if len(tenant.GetId()) == 0 {
25+
return "", nil
5426
}
55-
ret[data.Default] = name
56-
return ret, nil
27+
return fmt.Sprintf(d.format, tenant.GetId()), nil
5728
}

common/db_openner.go

-75
This file was deleted.

0 commit comments

Comments
 (0)