Skip to content

arash-mosavi/go-caching-system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go Caching System SDK

A production-ready, pluggable caching system SDK for Go applications with interface-first design and zero dependencies on concrete implementations.

Features

  • Interface-First Design: All caching logic depends on abstractions, not concrete implementations
  • Pluggable Backends: Built-in Redis and in-memory implementations, plus support for custom cache engines
  • Type Safety: Strict typing throughout - no interface{} usage
  • High-Level Client: JSON marshaling, string operations, GetOrSet patterns
  • Statistics: Built-in cache performance metrics
  • Production Ready: Modular, testable, and suitable for real production environments
  • Zero Circular Dependencies: Clean separation of layers and concerns

Quick Start

Using Built-in Redis Cache

package main

import (
    "context"
    "log"
    "time"

    "github.com/arash-mosavi/go-caching-system/pkg/gocache"
    "github.com/arash-mosavi/go-caching-system/config"
)

func main() {
    ctx := context.Background()

    // Create Redis cache with custom config
    redisConfig := config.DefaultRedisConfig()
    redisConfig.Address = "localhost:6379"
    redisConfig.Password = "your-password"
    
    cache, err := gocache.NewRedisCache(redisConfig)
    if err != nil {
        log.Fatal(err)
    }
    defer cache.Close()

    // String operations
    err = cache.SetString(ctx, "user:123", "John Doe", 5*time.Minute)
    if err != nil {
        log.Fatal(err)
    }

    name, err := cache.GetString(ctx, "user:123")
    if err != nil {
        log.Fatal(err)
    }
    
    log.Printf("User: %s", name)
}

Using In-Memory Cache

package main

import (
    "context"
    "log"
    "time"

    "github.com/arash-mosavi/go-caching-system/pkg/gocache"
)

func main() {
    ctx := context.Background()

    // Create in-memory cache
    cache := gocache.NewMemoryCache()
    defer cache.Close()

    // JSON operations
    type User struct {
        ID   int    `json:"id"`
        Name string `json:"name"`
        Age  int    `json:"age"`
    }

    user := User{ID: 123, Name: "John Doe", Age: 30}
    
    err := cache.SetJSON(ctx, "user:123", user, 10*time.Minute)
    if err != nil {
        log.Fatal(err)
    }

    var retrievedUser User
    err = cache.GetJSON(ctx, "user:123", &retrievedUser)
    if err != nil {
        log.Fatal(err)
    }
    
    log.Printf("User: %+v", retrievedUser)
}

Using Custom Cache Implementation

package main

import (
    "context"
    "log"
    "time"

    "github.com/arash-mosavi/go-caching-system/pkg/gocache"
    "github.com/arash-mosavi/go-caching-system/cache"
)

// Custom cache that implements cache.Cache interface
type MyCustomCache struct {
    // Your implementation here
    data map[string][]byte
}

func (c *MyCustomCache) Get(ctx context.Context, key string) ([]byte, error) {
    // Your implementation
    if value, exists := c.data[key]; exists {
        return value, nil
    }
    return nil, cache.ErrKeyNotFound
}

func (c *MyCustomCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
    // Your implementation
    if c.data == nil {
        c.data = make(map[string][]byte)
    }
    c.data[key] = value
    return nil
}

func (c *MyCustomCache) Delete(ctx context.Context, key string) error {
    delete(c.data, key)
    return nil
}

func (c *MyCustomCache) Exists(ctx context.Context, key string) (bool, error) {
    _, exists := c.data[key]
    return exists, nil
}

func (c *MyCustomCache) Flush(ctx context.Context) error {
    c.data = make(map[string][]byte)
    return nil
}

func (c *MyCustomCache) Close() error {
    return nil
}

func main() {
    ctx := context.Background()

    // Use your custom cache implementation
    customCache := &MyCustomCache{}
    cache := gocache.NewCustomCache(customCache)
    defer cache.Close()

    // Use the same high-level API
    err := cache.SetString(ctx, "key", "value", time.Hour)
    if err != nil {
        log.Fatal(err)
    }
}

Advanced Usage

GetOrSet Pattern

// Compute expensive operation only if not cached
result, err := cache.GetOrSetString(ctx, "expensive:key", func() (string, error) {
    // This function is only called if the key doesn't exist
    return computeExpensiveResult(), nil
}, 30*time.Minute)

Cache Statistics

// Get cache performance metrics (if supported by implementation)
if stats := cache.Stats(); stats != nil {
    fmt.Printf("Hit Rate: %.2f%%\n", stats.HitRate())
    fmt.Printf("Total Hits: %d\n", stats.TotalHits())
    fmt.Printf("Total Misses: %d\n", stats.TotalMisses())
    fmt.Printf("Total Operations: %d\n", stats.TotalOperations())
}

Architecture

The SDK follows a clean, layered architecture:

gocache.go            # Package entry point / Public API / Factory functions
pkg/
  - gocache/          # Core package implementation
client/               # High-level client with convenience methods  
cache/                # Core interfaces and error types
adapters/             # Cache implementation adapters
  - redis/            # Redis backend implementation
  - memory/           # In-memory backend implementation
config/               # Configuration types and validation
examples/             # Usage examples
  - basic/            # Basic usage examples
  - tagging/          # Tagging feature examples
  - versioning/       # Versioning feature examples
  - versioned_tagging/# Combined versioning and tagging examples
cmd/                  # Command-line applications
  - main.go           # Simple demo application
tests/                # Interface compliance and integration tests
run.sh                # Linux/macOS run script for examples and tests
run.bat               # Windows run script for examples and tests

Interface Design

All cache implementations must satisfy the cache.Cache interface:

type Cache interface {
    Get(ctx context.Context, key string) ([]byte, error)
    Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
    Delete(ctx context.Context, key string) error
    Exists(ctx context.Context, key string) (bool, error)
    Flush(ctx context.Context) error
    Close() error
}

Optional statistics interface:

type StatsCache interface {
    Cache
    HitRate() float64
    TotalHits() int64
    TotalMisses() int64
    TotalOperations() int64
}

Error Handling

The SDK provides structured error handling with cache.CacheError:

if err != nil {
    var cacheErr *cache.CacheError
    if errors.As(err, &cacheErr) {
        log.Printf("Cache operation '%s' failed for key '%s': %v", 
            cacheErr.Op, cacheErr.Key, cacheErr.Err)
    }
}

Running the Project

Using the Run Scripts

For Linux/macOS:

# Run the basic example
./run.sh basic

# Run all tests
./run.sh test

# Run the main application
./run.sh main

# Build and run everything
./run.sh all

For Windows:

# Run the basic example
run.bat basic

# Run all tests
run.bat test

# Run the main application
run.bat main

# Build and run everything
run.bat all

Manual Testing

Run all tests:

go test ./...

Run specific test packages:

go test ./tests/
go test ./adapters/redis/
go test ./adapters/memory/

Documentation

This project includes several documentation files:

  • README.md: Overview, quick start, and architecture
  • USAGE.md: Detailed usage guide and best practices
  • README-EXAMPLES.md: Examples and common use cases
  • CONTRIBUTING.md: Guidelines for contributing to the project

Requirements

  • Go 1.21+
  • Redis server (for Redis backend)

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published