Skip to content

Commit 665135b

Browse files
authored
Merge pull request #38 from ochom/dev
update ussd readme file
2 parents 4e29b44 + c26fd98 commit 665135b

19 files changed

+665
-378
lines changed

cache/cache.go

+50-10
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ package cache
33
import (
44
"sync"
55
"time"
6+
7+
"github.com/ochom/gutils/env"
8+
"github.com/ochom/gutils/logs"
69
)
710

11+
var cacheWorkers int
812
var memoryCache map[string]cacheItem
913
var mut sync.Mutex
1014

@@ -13,34 +17,42 @@ type V []byte
1317

1418
// cacheItem ...
1519
type cacheItem struct {
16-
key string
17-
value V
20+
value []byte
1821
createdAt time.Time
1922
expiry time.Duration
2023
callBack func()
2124
}
2225

26+
// expired ...
27+
func (c cacheItem) expired() bool {
28+
return time.Since(c.createdAt) > c.expiry
29+
}
30+
2331
func init() {
32+
cacheWorkers = env.Int("CACHE_TOTAL_WORKERS", 10)
2433
memoryCache = make(map[string]cacheItem)
2534
go CleanUp()
2635
}
2736

2837
// Set ...
2938
func Set(key string, value V) {
30-
SetWithExpiry(key, value, 0)
39+
expiry := time.Hour * time.Duration(env.Int("MAX_CACHE_HOUR", 24))
40+
SetWithExpiry(key, value, expiry)
3141
}
3242

3343
// SetWithExpiry ...
3444
func SetWithExpiry(key string, value V, expiry time.Duration) {
35-
SetWithCallback(key, value, expiry, nil)
45+
callback := func() {
46+
logs.Info("Session item expired: %s", key)
47+
}
48+
SetWithCallback(key, value, expiry, callback)
3649
}
3750

3851
// SetWithCallback ...
3952
func SetWithCallback(key string, value V, expiry time.Duration, callback func()) {
4053
mut.Lock()
4154
defer mut.Unlock()
4255
item := cacheItem{
43-
key: key,
4456
value: value,
4557
createdAt: time.Now(),
4658
expiry: expiry,
@@ -62,17 +74,45 @@ func Get(key string) V {
6274
return item.value
6375
}
6476

77+
// Delete ...
78+
func Delete(key string) {
79+
mut.Lock()
80+
defer mut.Unlock()
81+
delete(memoryCache, key)
82+
}
83+
6584
// CleanUp deletes expired cache items and calls their callbacks
6685
func CleanUp() {
67-
tk := time.NewTicker(time.Millisecond * 20)
86+
jobs := make(chan cacheItem, 100)
87+
88+
// start workers
89+
for i := 0; i < cacheWorkers; i++ {
90+
go runCallbacks(jobs)
91+
}
92+
93+
tick(jobs)
94+
}
95+
96+
// tick to run the ticker
97+
func tick(jobs chan<- cacheItem) {
98+
tk := time.NewTicker(time.Second) // every second
6899
for range tk.C {
100+
// acquire
101+
mut.Lock()
69102
for key, item := range memoryCache {
70-
if item.expiry > 0 && time.Since(item.createdAt) > item.expiry {
103+
if item.expired() {
104+
jobs <- item
71105
delete(memoryCache, key)
72-
if item.callBack != nil {
73-
item.callBack()
74-
}
75106
}
76107
}
108+
109+
// release
110+
mut.Unlock()
111+
}
112+
}
113+
114+
func runCallbacks(jobs <-chan cacheItem) {
115+
for item := range jobs {
116+
item.callBack()
77117
}
78118
}

cache/cache_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package cache
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestSetWithExpiry(t *testing.T) {
9+
type args struct {
10+
key string
11+
value V
12+
expiry time.Duration
13+
}
14+
tests := []struct {
15+
name string
16+
args args
17+
}{
18+
{
19+
name: "test 1",
20+
args: args{
21+
key: "test",
22+
value: V("value"),
23+
expiry: time.Second * 2,
24+
},
25+
},
26+
{
27+
name: "test 2",
28+
args: args{
29+
key: "test123",
30+
value: V("value"),
31+
expiry: time.Second * 1,
32+
},
33+
},
34+
}
35+
for _, tt := range tests {
36+
t.Run(tt.name, func(t *testing.T) {
37+
SetWithExpiry(tt.args.key, tt.args.value, tt.args.expiry)
38+
time.Sleep(tt.args.expiry + time.Second)
39+
40+
if _, ok := memoryCache[tt.args.key]; ok {
41+
t.Errorf("SetWithExpiry() = %v", ok)
42+
}
43+
})
44+
}
45+
}

env/env.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package env
2+
3+
import (
4+
"os"
5+
"strconv"
6+
7+
_ "github.com/joho/godotenv/autoload"
8+
"github.com/ochom/gutils/logs"
9+
)
10+
11+
// Get returns env variable or the provided default value when variable not found
12+
func Get(props ...string) string {
13+
if len(props) == 0 {
14+
logs.Error("Get Env: props cannot be empty")
15+
return ""
16+
}
17+
18+
key := props[0]
19+
defaultValue := ""
20+
if len(props) > 1 {
21+
defaultValue = props[1]
22+
}
23+
24+
value, ok := os.LookupEnv(key)
25+
if !ok {
26+
logs.Warn("Get Env: %s not found", key)
27+
return defaultValue
28+
}
29+
30+
return value
31+
}
32+
33+
// Int returns an integer from env variable or the provided default value when variable not found
34+
func Int(key string, defaultValue int) int {
35+
value, ok := os.LookupEnv(key)
36+
if !ok {
37+
return defaultValue
38+
}
39+
40+
val, err := strconv.Atoi(value)
41+
if err != nil {
42+
return defaultValue
43+
}
44+
45+
return val
46+
}
47+
48+
// Bool returns a boolean from env variable or the provided default value when variable not found
49+
func Bool(key string, defaultValue bool) bool {
50+
value, ok := os.LookupEnv(key)
51+
if !ok {
52+
return defaultValue
53+
}
54+
55+
val, err := strconv.ParseBool(value)
56+
if err != nil {
57+
return defaultValue
58+
}
59+
60+
return val
61+
}
62+
63+
// Float returns a float from env variable or the provided default value when variable not found
64+
func Float(key string, defaultValue float64) float64 {
65+
value, ok := os.LookupEnv(key)
66+
if !ok {
67+
return defaultValue
68+
}
69+
70+
val, err := strconv.ParseFloat(value, 64)
71+
if err != nil {
72+
return defaultValue
73+
}
74+
75+
return val
76+
}

0 commit comments

Comments
 (0)