Skip to content

Simple, fast, thread-safe in-memory cache with by-key TTL and generic value types

License

Notifications You must be signed in to change notification settings

parMaster/mcache

Repository files navigation

mcache codecov GitHub Go Report Card Go

mcache is a simple, fast, thread-safe in-memory cache library with by-key TTL written in Go.

Features

  • Thread-safe cache operations
  • Set key-value pairs with optional expiration time
  • Get values by key
  • Check if a key exists
  • Delete key-value pairs
  • Clear the entire cache
  • Cleanup expired key-value pairs
  • Generic type support

Installation

Use go get to install the package:

go get github.com/parMaster/mcache

Usage

Import the mcache package in your Go code:

import "github.com/parMaster/mcache"

Create a new cache instance using the NewCache constructor, and use it to perform cache operations:

cache := mcache.NewCache[string]()
data, err := cache.Get("key")
if err == nil {
	return data
}
data = ExpensiveFunctionCall()
cache.Set("key", data, 5*time.Minute) // cache data for 5 minutes

Examples

See the examples directory for more examples.

API Reference

Interface

The Cacher interface is used to define the cache operations:

type Cacher[T any] interface {
	Set(key string, value T, ttl time.Duration) bool
	Get(key string) (T, error)
	Has(key string) (bool, error)
	Del(key string) error
	Cleanup()
	Clear() error
}

Set

Set a key-value pair in the cache. The key must be a string, value type defined during cache creation, ttl is time.Duration type. If ttl is 0, the key-value pair will not expire.:

cache.Set("key", "value", time.Duration(0))

If the key already exists and is not expired, false will be returned. If the key exists but is expired, the value will be updated.

You can also set a key-value pair with an expiration time (in seconds):

cache.Set("key", "value", time.Minute)

The value will automatically expire after the specified duration.

Get

Retrieve a value from the cache by key:

value, err := cache.Get("key")
if err != nil {
    // handle error
}

If the key does not exist, an error mcache.ErrKeyNotFound will be returned. If the key exists but is expired, an error mcache.ErrExpired will be returned, and the key-value pair will be deleted.

Either error or value could be checked to determine if the key exists. Error is easier to check when the value is a zero value.

Has

Check if a key exists in the cache:

exists, err := cache.Has("key")
if err != nil {
    // handle error
}

if exists {
    // key exists
} else {
    // key does not exist
}

If the key exists but is expired, an error mcache.ErrExpired will be returned, and the key-value pair will be deleted.

Delete

Delete a key-value pair from the cache:

err := cache.Del("key")
if err != nil {
    // handle error
}

Clear

Clear the entire cache:

err := cache.Clear()
if err != nil {
    // handle error
}

Cleanup

Cleanup expired key-value pairs in the cache. You can call this method periodically to remove expired key-value pairs from the cache:

cache.Cleanup()

WithCleanup is a functional option to the NewCache constructor that allows you to specify a cleanup interval:

cache := mcache.NewCache(mcache.WithCleanup[string](time.Minute)) // cleanup every 60 seconds

It will basically run a Cleanup method in a goroutine with a time interval.

Tests and Benchmarks

100% test coverage:

$ go test -cover -race -cpu 24 .

ok      github.com/parMaster/mcache     8.239s  coverage: 100.0% of statements

Blinding fast and efficient:

$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/parMaster/mcache
cpu: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
BenchmarkWrite-4           	 1811689	       928.3 ns/op	     279 B/op	       2 allocs/op
BenchmarkRead-4            	 2925553	       445.8 ns/op	      15 B/op	       1 allocs/op
BenchmarkRWD-4             	 1351506	       881.6 ns/op	      47 B/op	       5 allocs/op
BenchmarkConcurrentRWD-4   	  452916	      2766 ns/op	     195 B/op	      16 allocs/op
PASS
ok  	github.com/parMaster/mcache	19.769s

Contributing

Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request.

License

This project is licensed under the MIT license.