This post writes a go version of caching layer using redis, wrapps it in a class and provides a usage.
Let’s create a Go package that provides a caching layer using Redis. We’ll use the popular go-redis/redis/v8 package to interact with Redis. We’ll define a Cache struct with methods to set and get values, and wrap the functionality in a class-like structure.
First, you need to install the go-redis/redis/v8 package:
go get github.com/go-redis/redis/v8
Redis Cache Layer in Go
Now, let’s create our caching layer in Go.
package cache import ( "context" "time" "github.com/go-redis/redis/v8" ) // Cache struct wraps the Redis client type Cache struct { client *redis.Client ctx context.Context } // NewCache creates a new Cache instance func NewCache(addr, password string, db int) *Cache { rdb := redis.NewClient(&redis.Options{ Addr: addr, Password: password, DB: db, }) return &Cache{ client: rdb, ctx: context.Background(), } } // Set sets a value in the cache with an expiration time func (c *Cache) Set(key string, value interface{}, expiration time.Duration) error { return c.client.Set(c.ctx, key, value, expiration).Err() } // Get gets a value from the cache func (c *Cache) Get(key string) (string, error) { val, err := c.client.Get(c.ctx, key).Result() if err == redis.Nil { return "", nil // Key does not exist } else if err != nil { return "", err } return val, nil } // Remove deletes a key from the cache func (c *Cache) Remove(key string) error { return c.client.Del(c.ctx, key).Err() } // Example usage func Example() { cache := NewCache("localhost:6379", "", 0) // Set a value with an expiration of 1 hour err := cache.Set("exampleKey", "exampleValue", time.Hour) if err != nil { panic(err) } // Get the value value, err := cache.Get("exampleKey") if err != nil { panic(err) } if value == "" { println("Key does not exist") } else { println("Value:", value) } }
Now, let’s use the caching layer in a simple main.go to demonstrate its usage.
package main import ( "fmt" "time" "your_module_name/cache" ) func main() { c := cache.NewCache("localhost:6379", "", 0) // Set a value in the cache err := c.Set("greeting", "Hello, World!", 10*time.Minute) if err != nil { fmt.Println("Error setting cache:", err) return } // Get the value from the cache val, err := c.Get("greeting") if err != nil { fmt.Println("Error getting cache:", err) return } fmt.Println("Cached value:", val) }
Here are some explanations:
- Cache struct: This wraps a Redis client and provides methods for interacting with Redis.
- NewCache function: This initializes a new Cache instance.
- Set method: This sets a key-value pair in the cache with an expiration time.
- Get method: This retrieves a value from the cache by its key.
- Example function: This demonstrates setting and getting a value using the cache.
To run this, ensure Redis is running on your local machine or the specified address, and replace “your_module_name” with the actual name of your module.
Start the Redis Server
To start Redis on your local machine, you have several options depending on your operating system. Here are the steps for the most common operating systems:
Run Redis On Linux
If you’re using a Linux distribution, you can typically install Redis using your package manager. For example, on Ubuntu, you can use:
1 2 | sudo apt update sudo apt install redis-server |
sudo apt update sudo apt install redis-server
Once installed, start the Redis server with:
1 | sudo service redis-server start |
sudo service redis-server start
Run Redis On macOS
If you have Homebrew installed, you can install Redis using:
1 2 | brew update
brew install redis |
brew update brew install redis
Start Redis using:
1 | brew services start redis |
brew services start redis
Run Redis On Windows
Redis is not natively supported on Windows, but you can use the Windows Subsystem for Linux (WSL) or install a Redis version specifically compiled for Windows from third-party sources.
You can start Redis Using WSL:
- Install WSL and a Linux distribution (like Ubuntu) from the Microsoft Store.
- Open the WSL terminal and follow the Linux instructions to install and start Redis.
- Using a precompiled Redis binary for Windows:
- Download Redis for Windows from the Microsoft Open Tech GitHub page.
- Extract the downloaded archive.
- Open Command Prompt and navigate to the extracted folder.
- Run the Redis server by executing: redis-server.exe
Run Redis using Docker Image
You can also use Docker to run Redis on any operating system: Make sure Docker is installed and running on your system.
Pull the Redis Docker image by:
1 | docker pull redis |
docker pull redis
Run the Redis container:
1 | docker run --name redis -d -p 6379:6379 redis |
docker run --name redis -d -p 6379:6379 redis
Verifying Redis is Running
To verify that Redis is running, you can use the Redis CLI:
1 | redis-cli ping |
redis-cli ping
You should get a response of PONG, indicating that the Redis server is up and running.
Using the Redis Server
Once Redis is running, you can use the Go code provided earlier to interact with the Redis server. Simply run your main.go and it should be able to connect to Redis and perform the set and get operations as demonstrated.
Tests the Cache Layer in Go
Testing in Go is straightforward using the testing package. Below, I’ll write a test file (Unit Test) for cache.go to test the Set and Get methods of the Cache struct. Make sure your Go module is properly set up to include the cache package, and that Redis is running on your local machine.
package cache import ( "testing" "time" ) // Helper function to create a new Cache for testing func newTestCache() *Cache { return NewCache("localhost:6379", "", 0) } func TestCache_SetAndGet(t *testing.T) { cache := newTestCache() // Test data key := "testKey" value := "testValue" expiration := 10 * time.Minute // Test setting a value in the cache err := cache.Set(key, value, expiration) if err != nil { t.Fatalf("Failed to set value in cache: %v", err) } // Test getting the value from the cache retrievedValue, err := cache.Get(key) if err != nil { t.Fatalf("Failed to get value from cache: %v", err) } if retrievedValue != value { t.Errorf("Expected value %s, but got %s", value, retrievedValue) } // Clean up err = cache.Remove(key) if err != nil { t.Fatalf("Failed to delete key from cache: %v", err) } } func TestCache_GetNonexistentKey(t *testing.T) { cache := newTestCache() // Test getting a value for a nonexistent key key := "nonexistentKey" value, err := cache.Get(key) if err != nil { t.Fatalf("Failed to get value from cache: %v", err) } if value != "" { t.Errorf("Expected empty value for nonexistent key, but got %s", value) } } func TestCache_SetWithExpiration(t *testing.T) { cache := newTestCache() // Test data key := "expiringKey" value := "value" expiration := 1 * time.Second // Test setting a value in the cache with expiration err := cache.Set(key, value, expiration) if err != nil { t.Fatalf("Failed to set value in cache: %v", err) } // Wait for the key to expire time.Sleep(2 * time.Second) // Test getting the value from the cache after expiration retrievedValue, err := cache.Get(key) if err != nil { t.Fatalf("Failed to get value from cache: %v", err) } if retrievedValue != "" { t.Errorf("Expected empty value for expired key, but got %s", retrievedValue) } } func TestCache_Remove(t *testing.T) { cache := newTestCache() // Test data key := "removeKey" value := "value" expiration := 10 * time.Minute // Test setting a value in the cache err := cache.Set(key, value, expiration) if err != nil { t.Fatalf("Failed to set value in cache: %v", err) } // Test removing the key from the cache err = cache.Remove(key) if err != nil { t.Fatalf("Failed to remove key from cache: %v", err) } // Test getting the value from the cache after removal retrievedValue, err := cache.Get(key) if err != nil { t.Fatalf("Failed to get value from cache: %v", err) } if retrievedValue != "" { t.Errorf("Expected empty value for removed key, but got %s", retrievedValue) } }
Here are some explanations:
- newTestCache Function: This is a helper function to create a new instance of the Cache for testing purposes.
- TestCache_SetAndGet:
- This test checks the Set and Get methods
- Sets a value in the cache.
- Retrieves the value from the cache and compares it to the original value.
- Cleans up by deleting the key from Redis.
- TestCache_GetNonexistentKey:
- This test checks the behavior of the Get method when the key does not exist.
- Tries to get a value for a nonexistent key and checks that the returned value is empty.
- TestCache_SetWithExpiration:
- This test checks that a key-value pair set with an expiration is correctly removed from the cache after the expiration time.
- Sets a value with a short expiration time.
- Waits for the expiration time to pass.
- Attempts to get the value from the cache and checks that it has been removed.
- TestCache_Remove: This test checks that the Remove function is correctedly implemented.
Running the Tests
To run the tests, use the go test command in the terminal:
1 | go test -v ./... |
go test -v ./...
The -v flag makes the output more verbose, so you can see which tests are running and their results. If everything is set up correctly, the tests should pass, indicating that the Cache struct is working as expected.
Example Output if Redis is Running
If Redis is running properly, you should see an output indicating that the tests have passed successfully:
=== RUN TestCache_SetAndGet --- PASS: TestCache_SetAndGet (0.05s) === RUN TestCache_GetNonexistentKey --- PASS: TestCache_GetNonexistentKey (0.01s) === RUN TestCache_SetWithExpiration --- PASS: TestCache_SetWithExpiration (1.05s) PASS ok your_module_name/caching 1.124s
This confirms that your Cache implementation is working correctly with Redis.
Project Structure of Cache Layer in Go
If you see this error: The error undefined: Cache typically occurs when the test file cannot find the Cache struct. This might be due to incorrect package naming or placement of the test file.
Project Structure: Ensure your project structure is correctly set up. It should look something like this:
your_module_name/ ├── cache/ │ ├── cache.go │ └── cache_test.go ├── go.mod └── main.go
Package Declaration: Make sure the cache.go and cache_test.go files declare the package cache.
go.mod: Ensure your go.mod file is properly set up. Replace your_module_name with your actual module name:
module your_module_name go 1.21 require ( github.com/go-redis/redis/v8 v8.11.5 )
–EOF (The Ultimate Computing & Technology Blog) —
loading...
Last Post: Why and How You Should Stop the ChatGPT and Bytedance Bots Crawling Your Pages?
Next Post: Teaching Kids Programming - The @cache Function Decorator in Python