echo_http_cache

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2025 License: MIT Imports: 21 Imported by: 1

README

Echo HTTP middleware for caching

This library is Echo HTTP middleware that provides server-side application layer caching for REST APIs.

I tried to use this library for my personal project to cache API response using a redis standalone server, but it didn't work well. The library hasn't been updated in a while and it seems like it is not maintained so I forked from https://github.com/SporkHubr/echo-http-cache and modified the code.

Features

  • Support different stores for caching:
    • Local memory (LRU, LFU, MRU, MFU algorithms)
    • Redis Standalone
    • Redis Cluster (NEW! ✨)
  • Enable to set different expiration time for each API
  • Enable to exclude any APIs not to cache

Installation

echo-http-cache supports 2 last Go versions and requires a Go version with modules support. So make sure to initialize a Go module as follows.

go mod init github.com/my/repo

And then install kenshin579/echo-http-cache

go get github.com/kenshin579/echo-http-cache

Requirements

  • Go 1.24 or higher
  • Echo v4
  • go-redis v8.11.5
  • Redis 6.0+ for Redis Cluster support

Quickstart

Here are three examples:

  • Using local memory
  • Using redis standalone server
  • Using redis cluster (NEW!)
Local Memory Example
package main

import (
	"net/http"
	"time"

	echoCacheMiddleware "github.com/kenshin579/echo-http-cache"
	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	e.Use(echoCacheMiddleware.CacheWithConfig(echoCacheMiddleware.CacheConfig{
		Store: echoCacheMiddleware.NewCacheMemoryStoreWithConfig(echoCacheMiddleware.CacheMemoryStoreConfig{
			Capacity:  5,
			Algorithm: echoCacheMiddleware.LFU,
		}),
		Expiration: 10 * time.Second,
	}))

	e.GET("/hello", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, Echo!")
	})

	e.Start(":8080")
}
Redis Standalone Example
package main

import (
	"net/http"
	"time"

	"github.com/go-redis/redis/v8"
	echoCacheMiddleware "github.com/kenshin579/echo-http-cache"
	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	e.Use(echoCacheMiddleware.CacheWithConfig(echoCacheMiddleware.CacheConfig{
		Store: echoCacheMiddleware.NewCacheRedisStoreWithConfig(redis.Options{
			Addr:     "localhost:6379",
			Password: "password",
		}),
		Expiration: 5 * time.Minute,
		IncludePathsWithExpiration: map[string]time.Duration{
			"/hello": 1 * time.Minute,
		},
		ExcludePaths: []string{"/ping"},
	}))

	e.GET("/hello", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, Echo!")
	})

	e.GET("/ping", func(c echo.Context) error {
		return c.String(http.StatusOK, "pong")
	})

	e.Start(":8080")
}
Redis Cluster Example
package main

import (
	"net/http"
	"time"

	"github.com/go-redis/redis/v8"
	echoCacheMiddleware "github.com/kenshin579/echo-http-cache"
	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	
	// Configure Redis Cluster store
	store := echoCacheMiddleware.NewCacheRedisClusterStoreWithConfig(redis.ClusterOptions{
		Addrs: []string{
			"localhost:17000",
			"localhost:17001", 
			"localhost:17002",
			"localhost:17003",
			"localhost:17004",
			"localhost:17005",
		},
	})
	
	e.Use(echoCacheMiddleware.CacheWithConfig(echoCacheMiddleware.CacheConfig{
		Store:      store,
		Expiration: 5 * time.Minute,
	}))

	e.GET("/api/data", func(c echo.Context) error {
		return c.JSON(http.StatusOK, map[string]interface{}{
			"data": "This response is cached in Redis Cluster",
			"time": time.Now().Format(time.RFC3339),
		})
	})

	e.Start(":8080")
}
Redis Cluster Cache

echo-http-cache now supports true Redis Cluster for distributed caching across multiple Redis nodes.

Note: If you're using Docker for local development, please be aware of known networking limitations that may affect Redis Cluster connectivity. These issues do not occur in production environments with properly configured Redis Cluster.

Docker Setup for Testing

A docker-compose.yml file is provided for local testing with Redis Cluster:

# Start Redis Cluster
docker-compose up -d

# Stop Redis Cluster
docker-compose down

The Docker setup includes:

  • 6 Redis nodes (3 masters, 3 replicas)
  • Ports mapped from 17000-17005 (to avoid conflicts with local Redis)
  • Automatic cluster initialization

Important: Due to Docker networking limitations on macOS and Windows, the cluster may not work correctly for caching operations. This is a known issue with Docker, not with the implementation.

Redis Cluster vs Redis Ring

Previously, this library used Redis Ring (client-side sharding) for cluster support. Now it uses actual Redis Cluster (server-side sharding) which provides:

  • ✅ Automatic failover
  • ✅ Native cluster support with 16384 hash slots
  • ✅ Better high availability
  • ✅ Easier node management
Migration from Redis Ring to Redis Cluster

If you were using the old Redis Ring implementation:

// Old (Redis Ring) - This was incorrectly named as cluster
store := echoCacheMiddleware.NewCacheRedisClusterWithConfig(redis.RingOptions{
    Addrs: map[string]string{
        "shard1": "localhost:17000",
        "shard2": "localhost:17001",
    },
})

// New (Redis Cluster) - True Redis Cluster support
store := echoCacheMiddleware.NewCacheRedisClusterStoreWithConfig(redis.ClusterOptions{
    Addrs: []string{
        "localhost:17000",
        "localhost:17001",
        "localhost:17002",
        "localhost:17003",
        "localhost:17004",
        "localhost:17005",
    },
})

⚠️ Breaking Change: The function name has changed from NewCacheRedisClusterWithConfig to NewCacheRedisClusterStoreWithConfig to maintain consistency with other store implementations.

Advanced Features

Clear Cache

For Redis Cluster, you can clear all cached data:

// Example: Clear cache endpoint
e.DELETE("/api/cache/clear", func(c echo.Context) error {
    if redisStore, ok := store.(*echoCacheMiddleware.CacheRedisClusterStore); ok {
        err := redisStore.Clear()
        if err != nil {
            return c.JSON(http.StatusInternalServerError, map[string]string{
                "error": "Failed to clear cache",
            })
        }
        return c.JSON(http.StatusOK, map[string]string{
            "message": "Cache cleared successfully",
        })
    }
    return c.JSON(http.StatusBadRequest, map[string]string{
        "error": "Store is not Redis Cluster",
    })
})

Note: The Clear() method iterates through all master nodes and executes FLUSHDB on each. This operation:

  • Is not atomic across the cluster
  • May take time proportional to the number of keys
  • Should be used sparingly in production

License

This code is released under the MIT License

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// DefaultCacheConfig defines default values for CacheConfig
	DefaultCacheConfig = CacheConfig{
		Skipper:    middleware.DefaultSkipper,
		Expiration: 3 * time.Minute,
	}
)
View Source
var DefaultCacheMemoryStoreConfig = CacheMemoryStoreConfig{
	Capacity:  10,
	Algorithm: LRU,
}

DefaultCacheMemoryStoreConfig provides default configuration values for CacheMemoryStoreConfig

View Source
var DefaultTwoLevelConfig = TwoLevelConfig{
	Strategy:     WriteThrough,
	L1TTL:        5 * time.Minute,
	L2TTL:        30 * time.Minute,
	CacheWarming: true,
	SyncMode:     Async,
	AsyncBuffer:  1000,
}

DefaultTwoLevelConfig provides default configuration

Functions

func Cache

func Cache(store CacheStore) echo.MiddlewareFunc

Cache returns a cache middleware

e := echo.New()

... 추가 설명

func CacheWithConfig

func CacheWithConfig(config CacheConfig) echo.MiddlewareFunc

CacheWithConfig returns a cache middleware

Types

type Algorithm

type Algorithm string

Algorithm is the string type for caching algorithms labels.

const (
	// LRU is the constant for Least Recently Used.
	LRU Algorithm = "LRU"

	// MRU is the constant for Most Recently Used.
	MRU Algorithm = "MRU"

	// LFU is the constant for Least Frequently Used.
	LFU Algorithm = "LFU"

	// MFU is the constant for Most Frequently Used.
	MFU Algorithm = "MFU"
)

type CacheConfig

type CacheConfig struct {
	// Skipper defines a function to skip middleware.
	Skipper middleware.Skipper

	Store CacheStore

	Expiration                 time.Duration // default expiration
	IncludePaths               []string
	IncludePathsWithExpiration map[string]time.Duration // key: path, value: expiration //IncludePathsWithExpiration has higher priority
	ExcludePaths               []string
}

CacheConfig data structure for HTTP cache middleware.

type CacheMemoryStore

type CacheMemoryStore struct {
	// contains filtered or unexported fields
}

CacheMemoryStore is the built-in store implementation for Cache

func NewCacheMemoryStore

func NewCacheMemoryStore() (store *CacheMemoryStore)

func NewCacheMemoryStoreWithConfig

func NewCacheMemoryStoreWithConfig(config CacheMemoryStoreConfig) (store *CacheMemoryStore)

func (*CacheMemoryStore) Clear added in v0.1.0

func (store *CacheMemoryStore) Clear() error

Clear removes all entries from the memory store

func (*CacheMemoryStore) Get

func (store *CacheMemoryStore) Get(key uint64) ([]byte, bool)

Get implements the cache Adapter interface Get method.

func (*CacheMemoryStore) Release

func (store *CacheMemoryStore) Release(key uint64)

Release implements the Adapter interface Release method.

func (*CacheMemoryStore) Set

func (store *CacheMemoryStore) Set(key uint64, response []byte, _ time.Time)

Set implements the cache Adapter interface Set method.

type CacheMemoryStoreConfig

type CacheMemoryStoreConfig struct {
	Capacity  int
	Algorithm Algorithm
}

CacheMemoryStoreConfig represents configuration for CacheMemoryStoreConfig

type CacheMetrics added in v0.1.0

type CacheMetrics struct {
	// contains filtered or unexported fields
}

CacheMetrics holds atomic counters for thread-safe statistics

func (*CacheMetrics) GetStats added in v0.1.0

func (m *CacheMetrics) GetStats() CacheStats

GetStats returns current statistics

func (*CacheMetrics) IncrementL1Hit added in v0.1.0

func (m *CacheMetrics) IncrementL1Hit()

IncrementL1Hit atomically increments L1 hit counter

func (*CacheMetrics) IncrementL2Hit added in v0.1.0

func (m *CacheMetrics) IncrementL2Hit()

IncrementL2Hit atomically increments L2 hit counter

func (*CacheMetrics) IncrementMiss added in v0.1.0

func (m *CacheMetrics) IncrementMiss()

IncrementMiss atomically increments miss counter

func (*CacheMetrics) Reset added in v0.1.0

func (m *CacheMetrics) Reset()

Reset resets all counters

type CacheRedisClusterStore

type CacheRedisClusterStore struct {
	// contains filtered or unexported fields
}

CacheRedisClusterStore is the redis cluster store implementation

func (*CacheRedisClusterStore) Clear added in v0.1.0

func (store *CacheRedisClusterStore) Clear() error

Clear removes all cache entries from all master nodes

func (*CacheRedisClusterStore) Get

func (store *CacheRedisClusterStore) Get(key uint64) ([]byte, bool)

Get implements the cache CacheRedisClusterStore interface Get method.

func (*CacheRedisClusterStore) Release

func (store *CacheRedisClusterStore) Release(key uint64)

Release implements the cache CacheRedisClusterStore interface Release method.

func (*CacheRedisClusterStore) Set

func (store *CacheRedisClusterStore) Set(key uint64, response []byte, expiration time.Time)

Set implements the cache CacheRedisClusterStore interface Set method.

type CacheRedisStore

type CacheRedisStore struct {
	// contains filtered or unexported fields
}

CacheRedisStore is the redis standalone store implementation for Cache

func (*CacheRedisStore) Clear added in v0.1.0

func (store *CacheRedisStore) Clear() error

Clear removes all entries from the Redis store Note: go-redis/cache library doesn't provide a direct Clear method This is a limitation of the underlying cache library

func (*CacheRedisStore) Get

func (store *CacheRedisStore) Get(key uint64) ([]byte, bool)

Get implements the cache CacheRedisStore interface Get method.

func (*CacheRedisStore) Release

func (store *CacheRedisStore) Release(key uint64)

func (*CacheRedisStore) Set

func (store *CacheRedisStore) Set(key uint64, response []byte, expiration time.Time)

type CacheResponse

type CacheResponse struct {
	// URL is URL
	URL string `json:"url"`

	// Header is the cached response header.
	Header http.Header `json:"header"`

	// Body is the cached response value.
	Body []byte `json:"body"`

	// Expiration is the cached response Expiration date.
	Expiration time.Time `json:"expiration"`

	// LastAccess is the last date a cached response was accessed.
	// Used by LRU and MRU algorithms.
	LastAccess time.Time `json:"lastAccess"`

	// Frequency is the count of times a cached response is accessed.
	// Used for LFU and MFU algorithms.
	Frequency int `json:"frequency"`
}

CacheResponse is the cached response data structure.

type CacheStats added in v0.1.0

type CacheStats struct {
	L1Hits       int64     `json:"l1Hits"`
	L2Hits       int64     `json:"l2Hits"`
	TotalMiss    int64     `json:"totalMiss"`
	TotalRequest int64     `json:"totalRequest"`
	HitRate      float64   `json:"hitRate"`
	L1HitRate    float64   `json:"l1HitRate"`
	L2HitRate    float64   `json:"l2HitRate"`
	L1Size       int       `json:"l1Size"`
	L2Size       int       `json:"l2Size"`
	LastUpdate   time.Time `json:"lastUpdate"`
}

CacheStats represents cache statistics

type CacheStore

type CacheStore interface {
	// Get retrieves the cached response by a given key. It also
	// returns true or false, whether it exists or not.
	Get(key uint64) ([]byte, bool)

	// Set caches a response for a given key until an Expiration date.
	Set(key uint64, response []byte, expiration time.Time)

	// Release frees cache for a given key.
	Release(key uint64)
}

CacheStore is the interface to be implemented by custom stores.

func NewCacheRedisClusterStore added in v0.1.0

func NewCacheRedisClusterStore() CacheStore

NewCacheRedisClusterStore creates a new Redis Cluster cache store with default config

func NewCacheRedisClusterStoreWithConfig added in v0.1.0

func NewCacheRedisClusterStoreWithConfig(opt redis.ClusterOptions) CacheStore

NewCacheRedisClusterStoreWithConfig creates a new Redis Cluster cache store

func NewCacheRedisStoreWithConfig

func NewCacheRedisStoreWithConfig(opt redis.Options) CacheStore

func NewCacheTwoLevelStore added in v0.1.0

func NewCacheTwoLevelStore(l1Store, l2Store CacheStore) CacheStore

NewCacheTwoLevelStore creates a new two-level cache store with default config

func NewCacheTwoLevelStoreWithConfig added in v0.1.0

func NewCacheTwoLevelStoreWithConfig(config TwoLevelConfig) CacheStore

NewCacheTwoLevelStoreWithConfig creates a new two-level cache store with custom config

type CacheStrategy added in v0.1.0

type CacheStrategy string

CacheStrategy defines the caching strategy for two-level cache

const (
	// WriteThrough writes to both L1 and L2 synchronously
	WriteThrough CacheStrategy = "WRITE_THROUGH"

	// WriteBack writes to L1 first, then L2 asynchronously
	WriteBack CacheStrategy = "WRITE_BACK"

	// CacheAside application manages cache explicitly
	CacheAside CacheStrategy = "CACHE_ASIDE"
)

type CacheTwoLevelStore added in v0.1.0

type CacheTwoLevelStore struct {
	// contains filtered or unexported fields
}

CacheTwoLevelStore implements two-level caching with L1 (memory) and L2 (Redis)

func (*CacheTwoLevelStore) ClearAll added in v0.1.0

func (store *CacheTwoLevelStore) ClearAll() error

ClearAll clears both L1 and L2 caches

func (*CacheTwoLevelStore) ClearL1 added in v0.1.0

func (store *CacheTwoLevelStore) ClearL1() error

ClearL1 clears only L1 cache

func (*CacheTwoLevelStore) ClearL2 added in v0.1.0

func (store *CacheTwoLevelStore) ClearL2() error

ClearL2 clears only L2 cache

func (*CacheTwoLevelStore) Get added in v0.1.0

func (store *CacheTwoLevelStore) Get(key uint64) ([]byte, bool)

Get implements CacheStore interface

func (*CacheTwoLevelStore) GetStats added in v0.1.0

func (store *CacheTwoLevelStore) GetStats() CacheStats

GetStats returns cache statistics

func (*CacheTwoLevelStore) Release added in v0.1.0

func (store *CacheTwoLevelStore) Release(key uint64)

Release implements CacheStore interface

func (*CacheTwoLevelStore) ResetStats added in v0.1.0

func (store *CacheTwoLevelStore) ResetStats()

ResetStats resets cache statistics

func (*CacheTwoLevelStore) Set added in v0.1.0

func (store *CacheTwoLevelStore) Set(key uint64, response []byte, expiration time.Time)

Set implements CacheStore interface

func (*CacheTwoLevelStore) Stop added in v0.1.0

func (store *CacheTwoLevelStore) Stop()

Stop gracefully stops the two-level cache store

type SyncMode added in v0.1.0

type SyncMode string

SyncMode defines the synchronization mode

const (
	// Sync - synchronous operations
	Sync SyncMode = "SYNC"

	// Async - asynchronous operations where possible
	Async SyncMode = "ASYNC"
)

type TwoLevelConfig added in v0.1.0

type TwoLevelConfig struct {
	L1Store      CacheStore
	L2Store      CacheStore
	Strategy     CacheStrategy
	L1TTL        time.Duration
	L2TTL        time.Duration
	CacheWarming bool
	SyncMode     SyncMode
	AsyncBuffer  int // Buffer size for async operations
}

TwoLevelConfig represents configuration for TwoLevelStore

Directories

Path Synopsis
two_level command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL