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.
8381type 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.
9290type 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).
369357func (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
0 commit comments