Skip to content

Commit ee21921

Browse files
committed
Removed Btree, added lookup table instead
1 parent e816743 commit ee21921

7 files changed

Lines changed: 192 additions & 218 deletions

File tree

cache.go

Lines changed: 46 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import (
55
"sync"
66
"sync/atomic"
77
"time"
8-
9-
"github.com/emirpasic/gods/trees/btree"
108
)
119

1210
// Internal cache errors
@@ -33,28 +31,28 @@ type AtomicCache struct {
3331
// Deadlock mutex for debugging purpose.
3432
// deadlock.RWMutex
3533

36-
// Lookup structure used for global index. It is based on BTree structure.
37-
lookup *btree.Tree
34+
// Lookup structure used for global index.
35+
lookup map[interface{}]LookupRecord
3836

3937
// Shards lookup tables which contains information about shards sections.
4038
smallShards, mediumShards, largeShards ShardsLookup
4139

4240
// Size of byte array used for memory allocation at small shard section.
43-
RecordSizeSmall uint32
41+
RecordSizeSmall int
4442
// Size of byte array used for memory allocation at medium shard section.
45-
RecordSizeMedium uint32
43+
RecordSizeMedium int
4644
// Size of byte array used for memory allocation at large shard section.
47-
RecordSizeLarge uint32
45+
RecordSizeLarge int
4846

4947
// Maximum records per shard.
50-
MaxRecords uint32
48+
MaxRecords int
5149

5250
// Maximum small shards which can be allocated in cache memory.
53-
MaxShardsSmall uint32
51+
MaxShardsSmall int
5452
// Maximum medium shards which can be allocated in cache memory.
55-
MaxShardsMedium uint32
53+
MaxShardsMedium int
5654
// Maximum large shards which can be allocated in cache memory.
57-
MaxShardsLarge uint32
55+
MaxShardsLarge int
5856

5957
// Garbage collector starter (run garbage collection every X memory sets).
6058
GcStarter uint32
@@ -72,25 +70,25 @@ type ShardsLookup struct {
7270
// Array of pointers to shard objects.
7371
shards []*Shard
7472
// Array of shard indexes which are currently active.
75-
shardsActive []uint32
73+
shardsActive []int
7674
// Array of shard indexes which are currently available for new allocation.
77-
shardsAvail []uint32
75+
shardsAvail []int
7876
}
7977

8078
// LookupRecord represents item in lookup table. One record contains index of
8179
// shard and record. So we can determine which shard access and which record of
8280
// shard to get. Record also contains expiration time.
8381
type LookupRecord struct {
84-
RecordIndex uint32
85-
ShardIndex uint32
86-
ShardSection uint8
82+
RecordIndex int
83+
ShardIndex int
84+
ShardSection int
8785
Expiration time.Time
8886
}
8987

9088
// BufferItem is used for buffer, which contains all unattended cache set
9189
// request.
9290
type BufferItem struct {
93-
Key []byte
91+
Key interface{}
9492
Data []byte
9593
Expire time.Duration
9694
}
@@ -105,7 +103,7 @@ func New(opts ...Option) *AtomicCache {
105103
MaxShardsSmall: 256,
106104
MaxShardsMedium: 128,
107105
MaxShardsLarge: 64,
108-
GcStarter: 25000,
106+
GcStarter: 512000,
109107
}
110108

111109
for _, opt := range opts {
@@ -116,7 +114,7 @@ func New(opts ...Option) *AtomicCache {
116114
cache := &AtomicCache{}
117115

118116
// Init lookup table
119-
cache.lookup = btree.NewWithStringComparator(16)
117+
cache.lookup = make(map[interface{}]LookupRecord)
120118

121119
// Init small shards section
122120
initShardsSection(&cache.smallShards, options.MaxShardsSmall, options.MaxRecords, options.RecordSizeSmall)
@@ -138,11 +136,11 @@ func New(opts ...Option) *AtomicCache {
138136

139137
// initShardsSection provides shards sections initialization. So the cache has
140138
// one shard in each section at the begging.
141-
func initShardsSection(shardsSection *ShardsLookup, maxShards, maxRecords, recordSize uint32) {
142-
var shardIndex uint32
139+
func initShardsSection(shardsSection *ShardsLookup, maxShards, maxRecords, recordSize int) {
140+
var shardIndex int
143141

144142
shardsSection.shards = make([]*Shard, maxShards)
145-
for i := uint32(0); i < maxShards; i++ {
143+
for i := 0; i < maxShards; i++ {
146144
shardsSection.shardsAvail = append(shardsSection.shardsAvail, i)
147145
}
148146

@@ -155,7 +153,7 @@ func initShardsSection(shardsSection *ShardsLookup, maxShards, maxRecords, recor
155153
// are replaced. If not, it checks if there are some allocated shard with empty
156154
// space for data. If there is no empty space, new shard is allocated. Otherwise
157155
// some valid record (FIFO queue) is deleted and new one is stored.
158-
func (a *AtomicCache) Set(key []byte, data []byte, expire time.Duration) error {
156+
func (a *AtomicCache) Set(key interface{}, data []byte, expire time.Duration) error {
159157
if len(data) > int(a.RecordSizeLarge) {
160158
return ErrDataLimit
161159
}
@@ -165,15 +163,13 @@ func (a *AtomicCache) Set(key []byte, data []byte, expire time.Duration) error {
165163
shardSection, shardSectionID := a.getShardsSectionBySize(len(data))
166164

167165
a.Lock()
168-
if ival, ok := a.lookup.Get(string(key)); !ok {
166+
if val, ok := a.lookup[key]; !ok {
169167
new = true
170168
} else {
171-
val := ival.(LookupRecord)
172-
173169
if val.ShardSection != shardSectionID {
174170
shardSection.shards[val.ShardIndex].Free(val.RecordIndex)
175171
val.RecordIndex = shardSection.shards[val.ShardIndex].Set(data)
176-
a.lookup.Put(string(key), LookupRecord{ShardIndex: val.ShardIndex, ShardSection: shardSectionID, RecordIndex: val.RecordIndex, Expiration: a.getExprTime(expire)})
172+
a.lookup[key] = LookupRecord{ShardIndex: val.ShardIndex, ShardSection: shardSectionID, RecordIndex: val.RecordIndex, Expiration: a.getExprTime(expire)}
177173
} else {
178174
prevShardSection := a.getShardsSectionByID(val.ShardSection)
179175
prevShardSection.shards[val.ShardIndex].Free(val.RecordIndex)
@@ -184,11 +180,11 @@ func (a *AtomicCache) Set(key []byte, data []byte, expire time.Duration) error {
184180
if new {
185181
if si, ok := a.getShard(shardSectionID); ok {
186182
ri := shardSection.shards[si].Set(data)
187-
a.lookup.Put(string(key), LookupRecord{ShardIndex: si, ShardSection: shardSectionID, RecordIndex: ri, Expiration: a.getExprTime(expire)})
183+
a.lookup[key] = LookupRecord{ShardIndex: si, ShardSection: shardSectionID, RecordIndex: ri, Expiration: a.getExprTime(expire)}
188184
} else if si, ok := a.getEmptyShard(shardSectionID); ok {
189185
shardSection.shards[si] = NewShard(a.MaxRecords, a.getRecordSizeByShardSectionID(shardSectionID))
190186
ri := shardSection.shards[si].Set(data)
191-
a.lookup.Put(string(key), LookupRecord{ShardIndex: si, ShardSection: shardSectionID, RecordIndex: ri, Expiration: a.getExprTime(expire)})
187+
a.lookup[key] = LookupRecord{ShardIndex: si, ShardSection: shardSectionID, RecordIndex: ri, Expiration: a.getExprTime(expire)}
192188
} else {
193189
if len(a.buffer) <= int(a.MaxRecords) {
194190
a.buffer = append(a.buffer, BufferItem{Key: key, Data: data, Expire: expire})
@@ -212,25 +208,17 @@ func (a *AtomicCache) Set(key []byte, data []byte, expire time.Duration) error {
212208

213209
// Get returns list of bytes if record is present in cache memory. If record is
214210
// not found, then error is returned and list is nil.
215-
func (a *AtomicCache) Get(key []byte) ([]byte, error) {
216-
var result []byte
217-
var hit = false
218-
211+
func (a *AtomicCache) Get(key interface{}) ([]byte, error) {
219212
a.RLock()
220-
if ival, ok := a.lookup.Get(string(key)); ok {
221-
val := ival.(LookupRecord)
222-
shardSection := a.getShardsSectionByID(val.ShardSection)
213+
val, ok := a.lookup[key]
214+
a.RUnlock()
223215

216+
if ok {
217+
shardSection := a.getShardsSectionByID(val.ShardSection)
224218
if shardSection.shards[val.ShardIndex] != nil && time.Now().Before(val.Expiration) {
225-
result = shardSection.shards[val.ShardIndex].Get(val.RecordIndex)
226-
hit = true
219+
return shardSection.shards[val.ShardIndex].Get(val.RecordIndex), nil
227220
}
228221
}
229-
a.RUnlock()
230-
231-
if hit {
232-
return result, nil
233-
}
234222

235223
return nil, ErrNotFound
236224
}
@@ -239,7 +227,7 @@ func (a *AtomicCache) Get(key []byte) ([]byte, error) {
239227
// if shard was released. The function requires the shard section ID and
240228
// shard ID on input.
241229
// This method is not thread safe and additional locks are required.
242-
func (a *AtomicCache) releaseShard(shardSectionID uint8, shard uint32) bool {
230+
func (a *AtomicCache) releaseShard(shardSectionID int, shard int) bool {
243231
var shardSection *ShardsLookup
244232

245233
if shardSection = a.getShardsSectionByID(shardSectionID); shardSection == nil {
@@ -267,7 +255,7 @@ func (a *AtomicCache) releaseShard(shardSectionID uint8, shard uint32) bool {
267255
// record. If there is no shard with available space, then false is returned as
268256
// a second value. The function requires the shard section ID on input.
269257
// This method is not thread safe and additional locks are required.
270-
func (a *AtomicCache) getShard(shardSectionID uint8) (uint32, bool) {
258+
func (a *AtomicCache) getShard(shardSectionID int) (int, bool) {
271259
var shardSection *ShardsLookup
272260

273261
if shardSection = a.getShardsSectionByID(shardSectionID); shardSection == nil {
@@ -287,7 +275,7 @@ func (a *AtomicCache) getShard(shardSectionID uint8) (uint32, bool) {
287275
// allocation. If there is no left index, then false is returned as a second
288276
// value. The function requires the shard section ID on input.
289277
// This method is not thread safe and additional locks are required.
290-
func (a *AtomicCache) getEmptyShard(shardSectionID uint8) (uint32, bool) {
278+
func (a *AtomicCache) getEmptyShard(shardSectionID int) (int, bool) {
291279
var shardSection *ShardsLookup
292280

293281
if shardSection = a.getShardsSectionByID(shardSectionID); shardSection == nil {
@@ -298,7 +286,7 @@ func (a *AtomicCache) getEmptyShard(shardSectionID uint8) (uint32, bool) {
298286
return 0, false
299287
}
300288

301-
var shardIndex uint32
289+
var shardIndex int
302290
shardIndex, shardSection.shardsAvail = shardSection.shardsAvail[0], shardSection.shardsAvail[1:]
303291
shardSection.shardsActive = append(shardSection.shardsActive, shardIndex)
304292

@@ -309,13 +297,13 @@ func (a *AtomicCache) getEmptyShard(shardSectionID uint8) (uint32, bool) {
309297
// identifier as a second value. The function requires the data size value on
310298
// input. If data are bigger than allowed value, then nil and 0 is returned.
311299
// This method is not thread safe and additional locks are required.
312-
func (a *AtomicCache) getShardsSectionBySize(dataSize int) (*ShardsLookup, uint8) {
300+
func (a *AtomicCache) getShardsSectionBySize(dataSize int) (*ShardsLookup, int) {
313301
if dataSize <= int(a.RecordSizeSmall) {
314-
return &a.smallShards, uint8(SMSH)
302+
return &a.smallShards, SMSH
315303
} else if dataSize > int(a.RecordSizeSmall) && dataSize <= int(a.RecordSizeMedium) {
316-
return &a.mediumShards, uint8(MDSH)
304+
return &a.mediumShards, MDSH
317305
} else if dataSize > int(a.RecordSizeMedium) && dataSize <= int(a.RecordSizeLarge) {
318-
return &a.largeShards, uint8(LGSH)
306+
return &a.largeShards, LGSH
319307
}
320308

321309
return nil, 0
@@ -325,12 +313,12 @@ func (a *AtomicCache) getShardsSectionBySize(dataSize int) (*ShardsLookup, uint8
325313
// requires the shard section ID on input. If section ID is not valid, nil
326314
// is returned.
327315
// This method is not thread safe and additional locks are required.
328-
func (a *AtomicCache) getShardsSectionByID(sectionID uint8) *ShardsLookup {
329-
if sectionID == uint8(SMSH) {
316+
func (a *AtomicCache) getShardsSectionByID(sectionID int) *ShardsLookup {
317+
if sectionID == SMSH {
330318
return &a.smallShards
331-
} else if sectionID == uint8(MDSH) {
319+
} else if sectionID == MDSH {
332320
return &a.mediumShards
333-
} else if sectionID == uint8(LGSH) {
321+
} else if sectionID == LGSH {
334322
return &a.largeShards
335323
}
336324

@@ -340,7 +328,7 @@ func (a *AtomicCache) getShardsSectionByID(sectionID uint8) *ShardsLookup {
340328
// getRecordSizeByShardSectionID returns maximum record size for specified
341329
// shard section ID. It returns 0 if there is not known section ID on input.
342330
// This method is not thread safe and additional locks are required.
343-
func (a *AtomicCache) getRecordSizeByShardSectionID(sectionID uint8) uint32 {
331+
func (a *AtomicCache) getRecordSizeByShardSectionID(sectionID int) int {
344332
if sectionID == SMSH {
345333
return a.RecordSizeSmall
346334
} else if sectionID == MDSH {
@@ -368,16 +356,14 @@ func (a *AtomicCache) getExprTime(expire time.Duration) time.Time {
368356
// active shard).
369357
func (a *AtomicCache) collectGarbage() {
370358
a.Lock()
371-
for _, k := range a.lookup.Keys() {
372-
iv, _ := a.lookup.Get(k.(string)) // get record
373-
v := iv.(LookupRecord) // convert record from interface to LookupRecord
359+
for k, v := range a.lookup {
374360
shardSection := a.getShardsSectionByID(v.ShardSection) // get shard section
375361
if time.Now().After(v.Expiration) {
376362
shardSection.shards[v.ShardIndex].Free(v.RecordIndex)
377363
if len(shardSection.shardsActive) > 1 {
378364
a.releaseShard(v.ShardSection, v.ShardIndex)
379365
}
380-
a.lookup.Remove(k)
366+
delete(a.lookup, k)
381367
}
382368
}
383369

cache_options.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ package atomiccache
33
// Options are used for AtomicCache construct function.
44
type Options struct {
55
// Size of byte array used for memory allocation at small shard section.
6-
RecordSizeSmall uint32
6+
RecordSizeSmall int
77
// Size of byte array used for memory allocation at medium shard section.
8-
RecordSizeMedium uint32
8+
RecordSizeMedium int
99
// Size of byte array used for memory allocation at large shard section.
10-
RecordSizeLarge uint32
10+
RecordSizeLarge int
1111
// Maximum records per shard.
12-
MaxRecords uint32
12+
MaxRecords int
1313
// Maximum small shards which can be allocated in cache memory.
14-
MaxShardsSmall uint32
14+
MaxShardsSmall int
1515
// Maximum medium shards which can be allocated in cache memory.
16-
MaxShardsMedium uint32
16+
MaxShardsMedium int
1717
// Maximum large shards which can be allocated in cache memory.
18-
MaxShardsLarge uint32
18+
MaxShardsLarge int
1919
// Garbage collector starter (run garbage collection every X sets).
2020
GcStarter uint32
2121
}
@@ -24,49 +24,49 @@ type Options struct {
2424
type Option func(*Options)
2525

2626
// OptionRecordSizeSmall option specification.
27-
func OptionRecordSizeSmall(option uint32) Option {
27+
func OptionRecordSizeSmall(option int) Option {
2828
return func(opts *Options) {
2929
opts.RecordSizeSmall = option
3030
}
3131
}
3232

3333
// OptionRecordSizeMedium option specification.
34-
func OptionRecordSizeMedium(option uint32) Option {
34+
func OptionRecordSizeMedium(option int) Option {
3535
return func(opts *Options) {
3636
opts.RecordSizeMedium = option
3737
}
3838
}
3939

4040
// OptionRecordSizeLarge option specification.
41-
func OptionRecordSizeLarge(option uint32) Option {
41+
func OptionRecordSizeLarge(option int) Option {
4242
return func(opts *Options) {
4343
opts.RecordSizeLarge = option
4444
}
4545
}
4646

4747
// OptionMaxRecords option specification.
48-
func OptionMaxRecords(option uint32) Option {
48+
func OptionMaxRecords(option int) Option {
4949
return func(opts *Options) {
5050
opts.MaxRecords = option
5151
}
5252
}
5353

5454
// OptionMaxShardsSmall option specification.
55-
func OptionMaxShardsSmall(option uint32) Option {
55+
func OptionMaxShardsSmall(option int) Option {
5656
return func(opts *Options) {
5757
opts.MaxShardsSmall = option
5858
}
5959
}
6060

6161
// OptionMaxShardsMedium option specification.
62-
func OptionMaxShardsMedium(option uint32) Option {
62+
func OptionMaxShardsMedium(option int) Option {
6363
return func(opts *Options) {
6464
opts.MaxShardsMedium = option
6565
}
6666
}
6767

6868
// OptionMaxShardsLarge option specification.
69-
func OptionMaxShardsLarge(option uint32) Option {
69+
func OptionMaxShardsLarge(option int) Option {
7070
return func(opts *Options) {
7171
opts.MaxShardsLarge = option
7272
}

0 commit comments

Comments
 (0)