Skip to content

Commit 3090891

Browse files
committed
push code
1 parent 1dca54d commit 3090891

11 files changed

+1100
-35
lines changed

batch.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,39 @@ import (
88
"strings"
99
)
1010

11-
func ExecuteStatements(ctx context.Context, db *sql.DB, sts []Statement) (int64, error) {
12-
return ExecuteBatch(ctx, db, sts, true, false)
11+
func ExecuteStatements(ctx context.Context, tx *sql.Tx, commit bool, stmts ...Statement) (int64, error) {
12+
if stmts == nil || len(stmts) == 0 {
13+
return 0, nil
14+
}
15+
var count int64
16+
count = 0
17+
for _, stmt := range stmts {
18+
r2, er3 := tx.ExecContext(ctx, stmt.Query, stmt.Params...)
19+
if er3 != nil {
20+
er4 := tx.Rollback()
21+
if er4 != nil {
22+
return count, er4
23+
}
24+
return count, er3
25+
}
26+
a2, er5 := r2.RowsAffected()
27+
if er5 != nil {
28+
tx.Rollback()
29+
return count, er5
30+
}
31+
count = count + a2
32+
}
33+
if commit {
34+
er6 := tx.Commit()
35+
return count, er6
36+
} else {
37+
return count, nil
38+
}
1339
}
14-
func ExecuteAll(ctx context.Context, db *sql.DB, stmts []Statement) (int64, error) {
40+
func ExecuteAll(ctx context.Context, db *sql.DB, stmts ...Statement) (int64, error) {
41+
if stmts == nil || len(stmts) == 0 {
42+
return 0, nil
43+
}
1544
tx, er1 := db.Begin()
1645
if er1 != nil {
1746
return 0, er1
@@ -127,9 +156,9 @@ type DefaultStatements struct {
127156

128157
func (s *DefaultStatements) Exec(ctx context.Context, db *sql.DB) (int64, error) {
129158
if s.SuccessFirst {
130-
return ExecuteStatements(ctx, db, s.Statements)
159+
return ExecuteBatch(ctx, db, s.Statements, true, false)
131160
} else {
132-
return ExecuteAll(ctx, db, s.Statements)
161+
return ExecuteAll(ctx, db, s.Statements...)
133162
}
134163
}
135164
func (s *DefaultStatements) Add(sql string, args []interface{}) Statements {
@@ -841,14 +870,14 @@ func UpdateBatch(ctx context.Context, db *sql.DB, tableName string, models inter
841870
if er1 != nil {
842871
return 0, er1
843872
}
844-
return ExecuteAll(ctx, db, stmts)
873+
return ExecuteAll(ctx, db, stmts...)
845874
}
846875
func SaveBatch(ctx context.Context, db *sql.DB, tableName string, models interface{}) (int64, error) {
847876
stmts, er1 := BuildToSaveBatch(db, tableName, models)
848877
if er1 != nil {
849878
return 0, er1
850879
}
851-
_, err := ExecuteAll(ctx, db, stmts)
880+
_, err := ExecuteAll(ctx, db, stmts...)
852881
total := int64(len(stmts))
853882
return total, err
854883
}

cache/cache_config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package cache
2+
3+
import "time"
4+
5+
// CacheConfig ...
6+
type CacheConfig struct {
7+
Size int64 `mapstructure:"size" json:"size,omitempty"` // byte
8+
CleaningEnable bool `mapstructure:"cleaning_enable" json:"cleaningEnable,omitempty"`
9+
CleaningInterval time.Duration `mapstructure:"cleaning_interval" json:"cleaningInterval,omitempty"` // nano-second
10+
}

cache/cache_service.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package cache
2+
3+
import (
4+
"database/sql"
5+
"time"
6+
)
7+
8+
type CacheService interface {
9+
Put(key string, obj interface{}, timeToLive time.Duration) error
10+
Expire(key string, timeToLive time.Duration) (bool, error)
11+
Get(key string) (*sql.Tx, error)
12+
ContainsKey(key string) (bool, error)
13+
Remove(key string) (bool, error)
14+
Clear() error
15+
GetMany(keys []string) (map[string]sql.Tx, []string, error)
16+
Keys() ([]string, error)
17+
Count() (int64, error)
18+
Size() (int64, error)
19+
}

cache/client.go

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
package cache
2+
3+
import (
4+
"errors"
5+
"sync"
6+
"unsafe"
7+
)
8+
9+
// LinearClient contains all attributes
10+
type Client struct {
11+
items sync.Map
12+
keys []string
13+
cleaningEnable bool
14+
linearSizes int64
15+
linearCurrentSizes int64
16+
rwMutex sync.RWMutex
17+
}
18+
19+
// NewClient return new instance
20+
func NewClient(linearSizes int64, cleaningEnable bool) *Client {
21+
currentLinear := Client{
22+
keys: []string{},
23+
items: sync.Map{},
24+
cleaningEnable: cleaningEnable,
25+
linearSizes: linearSizes,
26+
linearCurrentSizes: 0,
27+
rwMutex: sync.RWMutex{},
28+
}
29+
30+
return &currentLinear
31+
}
32+
33+
// Push item to the linear by key
34+
func (c *Client) Push(key string, value interface{}) error {
35+
itemSize := int64(unsafe.Sizeof(key)) + int64(unsafe.Sizeof(value))
36+
if itemSize > c.linearSizes || key == "" {
37+
return errors.New("key is empty or linear not enough space")
38+
}
39+
40+
// Clean space for new item
41+
if c.cleaningEnable {
42+
for c.linearCurrentSizes+itemSize > c.linearSizes {
43+
c.Take()
44+
}
45+
}
46+
47+
c.rwMutex.Lock()
48+
c.items.LoadOrStore(key, value)
49+
c.linearCurrentSizes += int64(unsafe.Sizeof(value))
50+
c.keys = append(c.keys, key)
51+
c.rwMutex.Unlock()
52+
53+
return nil
54+
}
55+
56+
// Pop return the last item from the linear and remove it
57+
func (c *Client) Pop() (interface{}, error) {
58+
if c.IsEmpty() {
59+
return nil, errors.New("the linear is empty")
60+
}
61+
62+
lastItemIndex := len(c.keys) - 1
63+
item, exits := c.items.Load(c.keys[lastItemIndex])
64+
if !exits {
65+
return nil, nil
66+
}
67+
68+
c.rwMutex.Lock()
69+
c.items.Delete(c.keys[lastItemIndex])
70+
c.linearCurrentSizes -= int64(unsafe.Sizeof(item))
71+
c.keys = removeItemByIndex(c.keys, lastItemIndex) //Update keys slice after remove that key from items map
72+
c.rwMutex.Unlock()
73+
74+
return item, nil
75+
}
76+
77+
// Take return the first item from the linear and remove it
78+
func (c *Client) Take() (interface{}, error) {
79+
if c.IsEmpty() {
80+
return nil, errors.New("the linear is empty")
81+
}
82+
83+
c.rwMutex.Lock()
84+
item, exits := c.items.Load(c.keys[0])
85+
if !exits {
86+
c.rwMutex.Unlock()
87+
return nil, nil
88+
}
89+
90+
c.items.Delete(c.keys[0])
91+
c.linearCurrentSizes -= int64(unsafe.Sizeof(item))
92+
c.keys = removeItemByIndex(c.keys, 0) //Update keys slice after remove that key from items map
93+
c.rwMutex.Unlock()
94+
95+
return item, nil
96+
}
97+
98+
// Get method return the item by key from linear and remove it
99+
// Goroutine: https://stackoverflow.com/questions/20945069/catching-return-values-from-goroutines
100+
func (c *Client) Get(key string) (interface{}, error) {
101+
if c.IsEmpty() {
102+
return nil, errors.New("linear is empty")
103+
}
104+
105+
var (
106+
wg sync.WaitGroup
107+
item interface{}
108+
itemExits bool
109+
itemIndex int
110+
itemIndexExits bool
111+
)
112+
113+
wg.Add(2)
114+
go func() {
115+
item, itemExits = c.items.Load(key)
116+
wg.Done()
117+
}()
118+
119+
go func() {
120+
itemIndex, itemIndexExits = findIndexByItem(key, c.keys)
121+
wg.Done()
122+
}()
123+
wg.Wait()
124+
125+
if itemExits && itemIndexExits {
126+
c.rwMutex.Lock()
127+
c.items.Delete(key)
128+
c.linearCurrentSizes -= int64(unsafe.Sizeof(item))
129+
c.keys = removeItemByIndex(c.keys, itemIndex) //Update keys slice after remove that key from items map
130+
c.rwMutex.Unlock()
131+
132+
return item, nil
133+
}
134+
135+
return nil, nil
136+
}
137+
138+
// Read method return the item by key from linear without remove it
139+
func (c *Client) Read(key string) (interface{}, error) {
140+
if c.IsEmpty() {
141+
return nil, errors.New("linear is empty")
142+
}
143+
144+
item, exits := c.items.Load(key)
145+
if !exits {
146+
return nil, nil
147+
}
148+
149+
return item, nil
150+
}
151+
152+
// Update reassign value to the key
153+
func (c *Client) Update(key string, value interface{}) error {
154+
newItemSize := int64(unsafe.Sizeof(key)) + int64(unsafe.Sizeof(value))
155+
if newItemSize > c.linearSizes || c.IsEmpty() {
156+
return errors.New("linear is empty or not enough space")
157+
}
158+
159+
c.rwMutex.Lock()
160+
currentSize, exits := c.IsExits(key)
161+
if !exits {
162+
c.rwMutex.Unlock()
163+
return errors.New("key does not exit")
164+
}
165+
c.items.Store(key, value)
166+
c.linearCurrentSizes += currentSize - newItemSize
167+
c.rwMutex.Unlock()
168+
169+
return nil
170+
}
171+
172+
// Range the LinearClient
173+
func (c *Client) Range(fn func(key, value interface{}) bool) {
174+
c.items.Range(fn)
175+
}
176+
177+
// IsExits check key exits or not
178+
func (c *Client) IsExits(key string) (int64, bool) {
179+
value, exits := c.items.Load(key)
180+
if !exits {
181+
return 0, false
182+
}
183+
184+
return int64(unsafe.Sizeof(key)) + int64(unsafe.Sizeof(value)), true
185+
}
186+
187+
// IsEmpty check linear size
188+
func (c *Client) IsEmpty() bool {
189+
return len(c.keys) == 0
190+
}
191+
192+
// GetItems return the map contain items
193+
func (c *Client) GetItems() sync.Map {
194+
return c.items
195+
}
196+
197+
// Getkeys return the list of key
198+
func (c *Client) Getkeys() []string {
199+
return c.keys
200+
}
201+
202+
// GetNumberOfKeys return the number of keys
203+
func (c *Client) GetNumberOfKeys() int {
204+
return len(c.keys)
205+
}
206+
207+
// GetLinearSizes return the linear size
208+
func (c *Client) GetLinearSizes() int64 {
209+
return c.linearSizes
210+
}
211+
212+
// SetLinearSizes change the linear size with new value
213+
func (c *Client) SetLinearSizes(linearSizes int64) {
214+
c.rwMutex.RLock()
215+
c.linearSizes = linearSizes
216+
c.rwMutex.RUnlock()
217+
}
218+
219+
// GetLinearCurrentSize return the current linear size
220+
func (c *Client) GetLinearCurrentSize() int64 {
221+
return c.linearCurrentSizes
222+
}
223+
224+
// removeItemByIndex remove item out of []string by index but maintains order, and return the new one
225+
// Source: https://yourbasic.org/golang/delete-element-slice/
226+
func removeItemByIndex(s []string, idx int) []string {
227+
copy(s[idx:], s[idx+1:]) // Shift s[idx+1:] left one index.
228+
s[len(s)-1] = "" // Erase last element (write zero value).
229+
return s[:len(s)-1] // Truncate s.
230+
}
231+
232+
// findIndexByItem return index belong to the key
233+
// Source: https://stackoverflow.com/questions/46745043/performance-of-for-range-in-go
234+
func findIndexByItem(keyName string, items []string) (int, bool) {
235+
for index := range items {
236+
if keyName == items[index] {
237+
return index, true
238+
}
239+
}
240+
241+
return -1, false
242+
}

cache/item.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package cache
2+
3+
// Item ...
4+
type Item struct {
5+
Data interface{} `json:"data,omitempty"`
6+
Expires int64 `json:"expires,omitempty"`
7+
}

0 commit comments

Comments
 (0)