1414 ErrFullMemory = errors .New ("cannot create new record: memory is full" )
1515)
1616
17- // Constans below are used for shard section identification.
17+ // Constants below are used for shard section identification.
1818const (
1919 // SMSH - Small Shards section
2020 SMSH = iota + 1
@@ -38,7 +38,7 @@ type AtomicCache struct {
3838 // Lookup structure used for global index.
3939 lookup map [string ]LookupRecord
4040
41- // Shards lookup tables which contains information about shards sections.
41+ // Shards lookup tables which contain information about shard sections.
4242 smallShards , mediumShards , largeShards ShardsLookup
4343
4444 // Size of byte array used for memory allocation at small shard section.
@@ -63,8 +63,8 @@ type AtomicCache struct {
6363 // Garbage collector counter for starter.
6464 GcCounter uint32
6565
66- // Buffer contains all unattended cache set requests. It has a maximum site
67- // which is equal to MaxRecords value.
66+ // Buffer contains all unattended cache set requests. It has a maximum size
67+ // which is equal to the MaxRecords value.
6868 buffer []BufferItem
6969}
7070
@@ -79,25 +79,25 @@ type ShardsLookup struct {
7979 shardsAvail []int
8080}
8181
82- // LookupRecord represents item in lookup table. One record contains index of
83- // shard and record. So we can determine which shard access and which record of
84- // shard to get. Record also contains expiration time.
82+ // LookupRecord represents an item in the lookup table. One record contains the index of
83+ // the shard and record. So we can determine which shard to access and which record of
84+ // the shard to get. Record also contains expiration time.
8585type LookupRecord struct {
8686 RecordIndex int
8787 ShardIndex int
8888 ShardSection int
8989 Expiration time.Time
9090}
9191
92- // BufferItem is used for buffer, which contains all unattended cache set
93- // request .
92+ // BufferItem is used for the buffer, which contains all unattended cache set
93+ // requests .
9494type BufferItem struct {
9595 Key string
9696 Data []byte
9797 Expire time.Duration
9898}
9999
100- // New initialize whole cache memory with one allocated shard.
100+ // New initializes the whole cache memory with one allocated shard.
101101func New (opts ... Option ) * AtomicCache {
102102 var options = & Options {
103103 RecordSizeSmall : 512 ,
@@ -138,8 +138,8 @@ func New(opts ...Option) *AtomicCache {
138138 return cache
139139}
140140
141- // initShardsSection provides shards sections initialization. So the cache has
142- // one shard in each section at the begging .
141+ // initShardsSection provides shard section initialization. So the cache has
142+ // one shard in each section at the beginning .
143143func initShardsSection (shardsSection * ShardsLookup , maxShards , maxRecords , recordSize int ) {
144144 var shardIndex int
145145
@@ -153,10 +153,12 @@ func initShardsSection(shardsSection *ShardsLookup, maxShards, maxRecords, recor
153153 shardsSection .shards [shardIndex ] = NewShard (maxRecords , recordSize )
154154}
155155
156- // Set store data to cache memory. If key/record is already in memory, then data
157- // are replaced. If not, it checks if there are some allocated shard with empty
158- // space for data. If there is no empty space, new shard is allocated. Otherwise
159- // some valid record (FIFO queue) is deleted and new one is stored.
156+ // Set stores data to cache memory. If the key/record is already in memory, then data
157+ // are replaced. If not, it checks if there is an allocated shard with empty
158+ // space for data. If there is no empty space, a new shard is allocated.
159+ // Remarks:
160+ // - If expiration time is set to 0 then maximum expiration time is used (48 hours).
161+ // - If expiration time is KeepTTL, then current expiration time is preserved.
160162func (a * AtomicCache ) Set (key string , data []byte , expire time.Duration ) error {
161163 // Reject if data is too large for any shard
162164 if len (data ) > int (a .RecordSizeLarge ) {
@@ -283,6 +285,48 @@ func (a *AtomicCache) Get(key string) ([]byte, error) {
283285 return nil , ErrNotFound
284286}
285287
288+ // Exists checks if record is present in cache memory. It returns true if record
289+ // is present, otherwise false.
290+ func (a * AtomicCache ) Exists (key string ) bool {
291+ a .RLock ()
292+ val , ok := a .lookup [key ]
293+ a .RUnlock ()
294+ if ! ok {
295+ return false
296+ }
297+ // Check expiration
298+ if time .Now ().After (val .Expiration ) {
299+ return false
300+ }
301+ return true
302+ }
303+
304+ // Delete removes record from cache memory. If record is not found, then error
305+ // is returned. It also releases memory used by record in shard.
306+ // If shard ends up empty, it is released.
307+ func (a * AtomicCache ) Delete (key string ) error {
308+ a .Lock ()
309+ defer a .Unlock ()
310+
311+ val , ok := a .lookup [key ]
312+ if ! ok {
313+ return ErrNotFound
314+ }
315+
316+ shardSection := a .getShardsSectionByID (val .ShardSection )
317+ // Check if the shard at val.ShardIndex is nil. This is a defensive check to
318+ // handle cases where the shard might have been released or not initialized
319+ // due to concurrent modifications or unexpected states.
320+ if shardSection .shards [val .ShardIndex ] != nil {
321+ shardSection .shards [val .ShardIndex ].Free (val .RecordIndex )
322+ a .releaseShard (val .ShardSection , val .ShardIndex )
323+ delete (a .lookup , key )
324+ return nil
325+ }
326+
327+ return ErrNotFound
328+ }
329+
286330// releaseShard release shard if there is no record in memory. It returns true
287331// if shard was released. The function requires the shard section ID and
288332// shard ID on input.
@@ -353,9 +397,9 @@ func (a *AtomicCache) getEmptyShard(shardSectionID int) (int, bool) {
353397 return shardIndex , true
354398}
355399
356- // getShardsSectionBySize returns shards section lookup structure and section
357- // identifier as a second value. The function requires the data size value on
358- // input. If data are bigger than allowed value, then nil and 0 is returned.
400+ // getShardsSectionBySize returns the shard section lookup structure and section
401+ // identifier as a second value. The function requires the data size value as input.
402+ // If data are bigger than the allowed value, then nil and 0 are returned.
359403// This method is not thread safe and additional locks are required.
360404func (a * AtomicCache ) getShardsSectionBySize (dataSize int ) (* ShardsLookup , int ) {
361405 if dataSize <= int (a .RecordSizeSmall ) {
@@ -412,10 +456,9 @@ func (a *AtomicCache) getExprTime(expire time.Duration) time.Time {
412456 return time .Now ().Add (expire )
413457}
414458
415- // collectGarbage provides garbage collect. It goes throught lookup table and
416- // checks expiration time. If shard end up empty, then garbage collect release
417- // him, but only if there is more than one shard in charge (we always have one
418- // active shard).
459+ // collectGarbage provides garbage collection. It goes through the lookup table and
460+ // checks expiration time. If a shard ends up empty, then garbage collection releases
461+ // it, but only if there is more than one shard in use (there is always at least one active shard).
419462func (a * AtomicCache ) collectGarbage () {
420463 a .Lock ()
421464 for k , v := range a .lookup {
0 commit comments