jetcache-go

repository·main·Indexed 19 days ago

https://github.com/mgtv-tech/jetcache-go

A production-grade, two-level caching framework for Go inspired by Java JetCache. It supports combining local caches (e.g., FreeCache, TinyLFU) with remote caches (e.g., Redis) and provides advanced features including singleflight-based miss protection to prevent cache stampedes, generic typed batch APIs (MGet) with pipeline optimization, and cache penetration protection using not-found placeholders.

Tokens
33.9K
Snippets
76
Records
155
Agent score
64%

What's inside jetcache-go

  1. What is jetcache-go?

    main

    jetcache-go is a production-grade cache framework for Go inspired by Java JetCache. It extends the go-redis/cache model by providing:

    • Two-level caching: Combines a local cache (e.g., FreeCache or TinyLFU) with a remote cache (e.g., Redis).
    • Singleflight miss protection: Uses singleflight to collapse multiple concurrent requests for the same missing key into a single load.
    • Typed batch APIs: Provides generic MGet with pipeline optimization.
    • Cache penetration protection: Uses a not-found placeholder strategy to prevent repeated lookups for non-existent keys.
    • Extensibility: Interface-driven design allows for custom local/remote providers, codecs, and stats implementations.
    • Observability: Built-in statistics and Prometheus plugin integration.
  2. Core Capabilities of jetcache-go

    main

    jetcache-go provides several key features for high-performance caching:

    • Two-Level Cache: Combines a local cache (using FreeCache or TinyLFU) with a remote cache (using Redis).
    • Singleflight: Merges concurrent requests for the same key into a single source call to prevent cache breakdown.
    • Generic MGet & Pipeline Optimization: Supports efficient batch queries using Go generics.
    • Cache Penetration Protection: Uses a 'not-found' placeholder strategy to prevent repeated lookups for non-existent keys.
    • Observability: Built-in statistics and integration with Prometheus plugins.
    • Extensibility: Interface-based design allows for custom local/remote cache implementations, codecs, and statistics providers.
  3. Overview of jetcache-go embedded components

    main

    jetcache-go uses an interface-driven design, allowing you to use built-in components or replace them with custom implementations. The core layers and their interfaces are:

    LayerInterfaceBuilt-in Implementations
    Local Cachelocal.Locallocal.NewTinyLFU, local.NewFreeCache
    Remote Cacheremote.Remoteremote.NewGoRedisV9Adapter
    Encoding/Decodingencoding.Codecmsgpack (default), json, sonic
    Metrics/Statsstats.Handlerstats.NewStatsLogger, multi-handler combinations
    Logginglogger.LoggerDefault implementation (replaceable)
  4. Understand jetcache-go core terminology

    main

    To effectively use jetcache-go and its troubleshooting guides, familiarize yourself with the following core concepts used throughout the documentation:

    • Cache Penetration (缓存穿透): When requests for non-existent data repeatedly miss the cache and hit the backend.
    • Cache Breakdown (缓存击穿/热点 key 击穿): When a high-concurrency request for a hot key occurs exactly when that key expires, causing a sudden spike in backend pressure.
    • Cache Avalanche (缓存雪崩): When a large number of keys expire or become unavailable simultaneously, overloading the backend.
    • Singleflight: A mechanism that ensures only one execution (e.g., a backend fetch) occurs for a specific key at any given moment, while other concurrent requests for the same key share the result.
    • Automatic Refresh (自动刷新): Background refreshing of selected keys to reduce the impact of sudden expirations.
    • Degradation (降级): Returning partially available capabilities when the cache or backend is experiencing errors.
    • Two-Level Cache (两级缓存): A layered read/write path consisting of a Local Cache and a Remote Cache.
    • Placeholder (not-found 占位符): A sentinel value used to represent "not found" in the cache to prevent Cache Penetration.
  5. Understand the jetcache-go component architecture

    main

    Architecture Overview

    jetcache-go is designed around interfaces, allowing you to use built-in components or swap them with your own implementations. The system is composed of several layers:

    LayerInterfaceBuilt-in Choices
    Local cachelocal.Locallocal.NewTinyLFU, local.NewFreeCache
    Remote cacheremote.Remoteremote.NewGoRedisV9Adapter
    Codecencoding.Codecmsgpack (default), json, sonic
    Metricsstats.Handlerstats.NewStatsLogger, multi-handler chain
    Logginglogger.Loggerdefault logger, replaceable

    By implementing these interfaces, you can customize how data is stored locally, how it is synchronized with remote stores, how it is serialized, and how the system reports metrics and logs.

  6. Determine the cache mode based on configuration

    main

    The operating mode of jetcache-go is determined by which backend options you provide during initialization:

    • Local Mode: Configure only WithLocal(...).
    • Remote Mode: Configure only WithRemote(...).
    • Two-Level Cache Mode: Configure both WithLocal(...) and WithRemote(...).
  7. How the Once execution model works

    main

    The Once method implements a Cache-aside pattern with singleflight protection to prevent multiple concurrent requests from hitting the source of truth simultaneously.

    Execution Flow:

    1. Read Local: Check the local cache.
    2. Miss Local? Check the remote cache.
    3. Miss Remote? Execute the Do(fn) function (protected by singleflight).
    4. Write Cache: The result from Do(fn) is written back to the cache.
    5. Return: The result is returned to the caller.

    If the key is found in the local or remote cache, the process returns immediately without executing Do(fn).

  8. Implement Two-Level Cache with `Once`

    main

    A two-level cache combines local and remote storage. The Once method is a powerful pattern that ensures a value is loaded only once (preventing cache stampedes) and handles the orchestration between cache layers and the data source.

    c.Once(ctx, key, valuePtr, options):

    • If the key exists in local or remote cache, it populates valuePtr and returns.
    • If not, it executes the function provided in cache.Do(...).
    • The result of cache.Do is then stored in both cache layers.
    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    
    	cache "github.com/mgtv-tech/jetcache-go"
    	"github.com/mgtv-tech/jetcache-go/local"
    	"github.com/mgtv-tech/jetcache-go/remote"
    	"github.com/redis/go-redis/v9"
    )
    
    func main() {
    	rdb := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
    	c := cache.New(
    		cache.WithName("demo-both"),
    		cache.WithLocal(local.NewTinyLFU(100_000, time.Minute)),
    		cache.WithRemote(remote.NewGoRedisV9Adapter(rdb)),
    	)
    	defer c.Close()
    
    	var user string
    	err := c.Once(context.Background(), "user:1001",
    		cache.Value(&user),
    		cache.Do(func(context.Context) (any, error) {
    			fmt.Println("load from DB")
    			return "alice", nil
    		}),
    	)
    	if err != nil {
    		panic(err)
    	}
    }
  9. How Auto-Refresh works

    main

    Auto-refresh is a key-level, opt-in feature enabled via cache.Refresh(true). It uses a scheduler and a distributed lock (via Redis) to ensure only one node updates a hot key at a time.

    Refresh Lifecycle:

    1. The scheduler triggers a refresh for a key.
    2. The node attempts to acquire a Redis lock for that key.
    3. If lock is acquired: The node loads the latest value from the backend and updates both local and remote caches.
    4. If lock is denied: The node skips the refresh round to avoid redundant work.

    Usage Recommendation: Only enable auto-refresh for a small set of 'hot keys' that have expensive loader functions.

  10. Implement custom interfaces for jetcache-go

    main

    To extend jetcache-go with non-official integrations, you must implement one of the following core interfaces:

    • Custom Remote Store: Implement remote.Remote.
    • Custom Local Cache Engine: Implement local.Local.
    • Custom Codec: Implement encoding.Codec and register it using encoding.RegisterCodec(...).
    • Custom Observability Backend: Implement stats.Handler.
  11. How the read path and Singleflight work together

    main

    To protect your backend services from high concurrency during cache misses, jetcache-go uses a Once(...) + Do(...) pattern. This mechanism leverages Singleflight to merge multiple concurrent requests for the same key into a single call to your data source.

    The Read Flow:

    1. Local Hit? If yes, return immediately.
    2. Remote Hit? If yes, backfill the local cache and return.
    3. Cache Miss? If both fail, the request enters the Singleflight layer.
    4. Singleflight Execution: Only one execution of the Do(...) function is triggered for the specific key. All other concurrent callers wait for this single result.
    5. Result Handling:
      • Success: The result is written to both remote and local caches and returned to all callers.
      • NotFound: A 'not-found' placeholder is written to the cache to prevent future cache stampedes for non-existent keys.
      • Error: The error is returned to the callers.
  12. Choose a cache mode for your application

    main

    jetcache-go provides three distinct caching modes depending on your latency and consistency requirements:

    • local: Uses only in-process cache. It offers the lowest latency but does not share data across different application nodes.
    • remote: Uses only a remote cache (e.g., Redis). This provides stronger consistency across multiple nodes.
    • both: Uses both local and remote caches. This is the recommended mode for high QPS (Queries Per Second) read interfaces to balance speed and distributed consistency.