Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add setOrGet Function to Cache Implementation #383

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

ndjuric-bit
Copy link

Overview:

This pull request introduces a new feature to our cache implementation by adding the SetOrGet function. This function allows clients to efficiently set a cache entry if it doesn't exist or retrieve the existing value associated with a given key. It returns the actual value, a boolean indicating whether the entry was loaded from the cache or not, and any potential error.

Function Signature::

SetOrGet(key string, entry []byte) (actual []byte, loaded bool, err error)

Parameters:

  • key: A string representing the cache key to look up.
  • entry: A byte slice containing the value to set in the cache if the key doesn't exist.

Return Values:

  • actual: The actual value associated with the key in the cache, which may be the newly set value or the existing cached value.
  • loaded: A boolean indicating whether the entry was loaded from the cache (true) or if it was set with the provided entry (false).
  • err: An error that is returned in case of any issues during the set or get operation.

Example:

actualValue, loadedFromCache, err := cache.SetOrGet("my_key", []byte("new_value"))
if err != nil {
    // Handle the error.
} else {
    if loadedFromCache {
        // The value was loaded from the cache.
        fmt.Printf("Value for key 'my_key' found in the cache: %s\n", actualValue)
    } else {
        // The value was set in the cache.
        fmt.Printf("Value for key 'my_key' was set to: %s\n", actualValue)
    }
}

Testing:

This pull request includes unit tests to ensure the correct behavior of the SetOrGet function. The tests cover various scenarios, including cache hits and cache misses.

Impact on Existing Code:

This change introduces a new function to cache system. Existing code that uses the cache may benefit from this function for efficient get-or-set operations, but it should not negatively impact the existing functionality. This change is designed to be backward compatible.

Documentation:

The function's usage has been documented, and code comments have been updated to reflect the introduction of the SetOrGet function.

Reviewer Instructions:

Please review the code changes and documentation to ensure that the SetOrGet function is correctly implemented and well-documented. Verify that the function behaves as expected, handles errors gracefully, and is a useful addition to our cache system.
In case of hash collision behaviour of get function is held plus old data is deleted and new one is stored

Testing Instructions:
Verify that the unit tests pass and consider adding additional test cases as needed to cover any edge cases.

Changelog:

  • Added SetOrGet function to the cache module.
    
  • Updated documentation and comments to include usage examples.
    

@codecov-commenter
Copy link

codecov-commenter commented Nov 2, 2023

Codecov Report

Merging #383 (dd4212d) into main (58fe2d2) will increase coverage by 0.27%.
The diff coverage is 82.75%.

❗ Current head dd4212d differs from pull request most recent head 4c79098. Consider uploading reports for the commit 4c79098 to get more accurate results

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #383      +/-   ##
==========================================
+ Coverage   88.66%   88.94%   +0.27%     
==========================================
  Files          15       15              
  Lines         794      823      +29     
==========================================
+ Hits          704      732      +28     
  Misses         76       76              
- Partials       14       15       +1     
Files Coverage Δ
bigcache.go 93.18% <100.00%> (+0.21%) ⬆️
shard.go 90.97% <80.00%> (+0.47%) ⬆️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 58fe2d2...4c79098. Read the comment docs.

shard.go Outdated Show resolved Hide resolved
shard.go Outdated
Comment on lines 183 to 184
s.lock.RUnlock()
s.lock.Lock()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is problematic. We have a read lock and then we lock to insert something. Another goroutine might hijack our lock in meantime.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for answer
i think i found somewhere that this actions(locks) are atomic and in that case will not be possible to jump between them and with performance in mind i have sent code you have seen. Original code is below it is more readable and if you sad more robust, than i will change request with code below.

Original code was

func (s *cacheShard) setOrGet(key string, hashedKey uint64, entry []byte) (actual []byte, loaded bool, err error) {
	s.lock.Lock()
	defer s.lock.Unlock()

	wrappedEntry, err := s.getWrappedEntry(hashedKey)
	if err == nil {
		if entryKey := readKeyFromEntry(wrappedEntry); key == entryKey {
			actual = readEntry(wrappedEntry)
			s.hit(hashedKey)
			return actual, true, nil
		} else {

			s.collision()
			if s.isVerbose {
				s.logger.Printf("Collision detected. Both %q and %q have the same hash %x", key, entryKey, hashedKey)
			}

			delete(s.hashmap, hashedKey)
			s.onRemove(wrappedEntry, Deleted)
			if s.statsEnabled {
				delete(s.hashmapStats, hashedKey)
			}
			resetHashFromEntry(wrappedEntry)
		}
	} else if !errors.Is(err, ErrEntryNotFound) {
		return entry, false, err
	}

	return entry, false, s.addNewWithoutLock(key, hashedKey, entry)
}

@janisz
Copy link
Collaborator

janisz commented Nov 2, 2023

@ndjuric-bit Thanks for this. I think we need to solve issue with concurrent updates during setOrGet with current implementation it's not locked so there might an override. I think this would be a rare case but possible so it needs to be documented. I also miss some tests how it behaves when error happens.

@ndjuric-bit
Copy link
Author

@janisz Is there something needed from my side more ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants