{"id":37187861,"url":"https://github.com/actforgood/xcache","last_synced_at":"2026-01-14T21:50:47.915Z","repository":{"id":54113987,"uuid":"521798279","full_name":"actforgood/xcache","owner":"actforgood","description":"Golang Caching Package","archived":false,"fork":false,"pushed_at":"2024-06-10T13:50:29.000Z","size":153,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-06-21T12:56:01.646Z","etag":null,"topics":["cache","go","memory-cache","redis-cache","xcache"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/actforgood.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-05T23:16:03.000Z","updated_at":"2024-05-17T12:54:44.000Z","dependencies_parsed_at":"2022-08-13T06:51:05.427Z","dependency_job_id":"e385c887-3fdc-4c28-849c-1ad4867e7b42","html_url":"https://github.com/actforgood/xcache","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/actforgood/xcache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/actforgood","download_url":"https://codeload.github.com/actforgood/xcache/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxcache/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28436217,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T21:32:52.117Z","status":"ssl_error","status_checked_at":"2026-01-14T21:32:33.442Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","go","memory-cache","redis-cache","xcache"],"created_at":"2026-01-14T21:50:47.087Z","updated_at":"2026-01-14T21:50:47.903Z","avatar_url":"https://github.com/actforgood.png","language":"Go","readme":"# Xcache\n\n[![Build Status](https://github.com/actforgood/xcache/actions/workflows/build.yml/badge.svg)](https://github.com/actforgood/xcache/actions/workflows/build.yml)\n[![License](https://img.shields.io/badge/license-MIT-blue)](https://raw.githubusercontent.com/actforgood/xcache/main/LICENSE)\n[![Coverage Status](https://coveralls.io/repos/github/actforgood/xcache/badge.svg?branch=main)](https://coveralls.io/github/actforgood/xcache?branch=main)\n[![Goreportcard](https://goreportcard.com/badge/github.com/actforgood/xcache)](https://goreportcard.com/report/github.com/actforgood/xcache)\n[![Go Reference](https://pkg.go.dev/badge/github.com/actforgood/xcache.svg)](https://pkg.go.dev/github.com/actforgood/xcache)  \n\n---\n\nPackage `xcache` offers caching alternatives for an application like a local in memory cache,\nor distributed Redis cache, or combination of those two in a multi layered cache.\n\n### Installation\n\n```shell\n$ go get github.com/actforgood/xcache\n```\n\n\n### Cache adapters\n- `Memory` - a local in memory cache, relies upon Freecache package.  \n- `Redis6` - Redis version 6 cache (single instance / sentinel failover / cluster).  \n- `Redis7` - Redis version 7 cache (single instance / sentinel failover / cluster).  \n- `Multi` - A multi layer cache.  \n- `Nop` - A no-operation cache.  \n- `Mock` - A stub that can be used in Unit Tests.  \n\n\n### The Cache contract\nLooks like:  \n```go\n// Cache provides prototype a for storing and returning a key-value into/from cache.\ntype Cache interface {\n\t// Save stores the given key-value with expiration period into cache.\n\t// An expiration period equal to 0 (NoExpire) means no expiration.\n\t// A negative expiration period triggers deletion of key.\n\t// It returns an error if the key could not be saved.\n\tSave(ctx context.Context, key string, value []byte, expire time.Duration) error\n\n\t// Load returns a key's value from cache, or an error if something bad happened.\n\t// If the key is not found, ErrNotFound is returned.\n\tLoad(ctx context.Context, key string) ([]byte, error)\n\n\t// TTL returns a key's remaining time to live, or an error if something bad happened.\n\t// If the key is not found, a negative TTL is returned.\n\t// If the key has no expiration, 0 (NoExpire) is returned.\n\tTTL(ctx context.Context, key string) (time.Duration, error)\n\n\t// Stats returns some statistics about cache's memory/keys.\n\t// It returns an error if something goes wrong.\n\tStats(context.Context) (Stats, error)\n}\n```\n\n### Examples\n###### Memory\n```go\nfunc ExampleMemory() {\n\tcache := xcache.NewMemory(10 * 1024 * 1024) // 10 Mb\n\n\tctx := context.Background()\n\tkey := \"example-memory\"\n\tvalue := []byte(\"Hello Memory Cache\")\n\tttl := 10 * time.Minute\n\n\t// save a key for 10 minutes\n\tif err := cache.Save(ctx, key, value, ttl); err != nil {\n\t\tfmt.Println(\"could not save Memory cache key: \" + err.Error())\n\t}\n\n\t// load the key's value\n\tif value, err := cache.Load(ctx, key); err != nil {\n\t\tfmt.Println(\"could not get Memory cache key: \" + err.Error())\n\t} else {\n\t\tfmt.Println(string(value))\n\t}\n\n\t// Output:\n\t// Hello Memory Cache\n}\n```\nBenchmarks\n```shell\ngo test -run=^# -benchmem -benchtime=5s -bench BenchmarkMemory github.com/actforgood/xcache\ngoos: linux\ngoarch: amd64\npkg: github.com/actforgood/xcache\ncpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz\nBenchmarkMemory_Save-4                  11463078               492.5 ns/op             2 B/op          0 allocs/op\nBenchmarkMemory_Save_parallel-4         24244593               261.4 ns/op            25 B/op          1 allocs/op\nBenchmarkMemory_Load-4                  30390457               194.0 ns/op            40 B/op          2 allocs/op\nBenchmarkMemory_Load_parallel-4         25545855               220.0 ns/op            40 B/op          2 allocs/op\nBenchmarkMemory_TTL-4                   55357045               110.5 ns/op             0 B/op          0 allocs/op\nBenchmarkMemory_TTL_parallel-4          40464970               153.2 ns/op             0 B/op          0 allocs/op\nBenchmarkMemory_Stats-4                  5760609               983.7 ns/op             0 B/op          0 allocs/op\nBenchmarkMemory_Stats_parallel-4        23939924               254.5 ns/op             0 B/op          0 allocs/op\n```\n\n###### Redis\n```go\nfunc ExampleRedis() {\n\tcache := xcache.NewRedis6(xcache.RedisConfig{ // or xcache.NewRedis7 if you're using ver. 7\n\t\tAddrs: []string{\"127.0.0.1:6379\"},\n\t})\n\n\tctx := context.Background()\n\tkey := \"example-redis\"\n\tvalue := []byte(\"Hello Redis Cache\")\n\tttl := 10 * time.Minute\n\n\t// save a key for 10 minutes\n\tif err := cache.Save(ctx, key, value, ttl); err != nil {\n\t\tfmt.Println(\"could not save Redis cache key: \" + err.Error())\n\t}\n\n\t// load the key's value\n\tif value, err := cache.Load(ctx, key); err != nil {\n\t\tfmt.Println(\"could not get Redis cache key: \" + err.Error())\n\t} else {\n\t\tfmt.Println(string(value))\n\t}\n\n\t// close the cache when no needed anymore/at your application shutdown.\n\tif err := cache.Close(); err != nil {\n\t\tfmt.Println(\"could not close Redis cache: \" + err.Error())\n\t}\n\n\t// should output:\n\t// Hello Redis Cache\n}\n```\nBenchmarks\n```shell\ngo test -tags=integration -run=^# -benchmem -benchtime=5s -bench BenchmarkRedis github.com/actforgood/xcache\ngoos: linux\ngoarch: amd64\npkg: github.com/actforgood/xcache\ncpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz\nBenchmarkRedis6_Save_integration-4                 32479            161610 ns/op             272 B/op          7 allocs/op\nBenchmarkRedis6_Save_parallel_integration-4       130350             43058 ns/op             296 B/op          8 allocs/op\nBenchmarkRedis6_Load_integration-4                 39960            144686 ns/op             208 B/op          6 allocs/op\nBenchmarkRedis6_Load_parallel_integration-4       103783             62320 ns/op             208 B/op          6 allocs/op\nBenchmarkRedis6_TTL_integration-4                  43336            158656 ns/op             196 B/op          5 allocs/op\nBenchmarkRedis6_TTL_parallel_integration-4        135324             43266 ns/op             196 B/op          5 allocs/op\nBenchmarkRedis6_Stats-4                            23257            244013 ns/op            5052 B/op          6 allocs/op\nBenchmarkRedis6_Stats_parallel-4                   63873             90896 ns/op            5052 B/op          6 allocs/op\nBenchmarkRedis7_Save_integration-4                 38620            162062 ns/op             312 B/op         10 allocs/op\nBenchmarkRedis7_Save_parallel_integration-4       129525             46068 ns/op             336 B/op         11 allocs/op\nBenchmarkRedis7_Load_integration-4                 42074            153150 ns/op             248 B/op          9 allocs/op\nBenchmarkRedis7_Load_parallel_integration-4       139232             43403 ns/op             248 B/op          9 allocs/op\nBenchmarkRedis7_TTL_integration-4                  32029            163338 ns/op             236 B/op          8 allocs/op\nBenchmarkRedis7_TTL_parallel_integration-4        117226             56544 ns/op             236 B/op          8 allocs/op\nBenchmarkRedis7_Stats-4                            23600            254668 ns/op            5604 B/op          9 allocs/op\nBenchmarkRedis7_Stats_parallel-4                   59908            100755 ns/op            5604 B/op          9 allocs/op\n```\n\n###### Multi\n```go\nfunc ExampleMulti() {\n\t// create a frontend - backend multi cache.\n\tfrontCache := xcache.NewMemory(10 * 1024 * 1024) // 10 Mb\n\tbackCache := xcache.NewRedis6(xcache.RedisConfig{\n\t\tAddrs: []string{\"127.0.0.1:6379\"},\n\t})\n\tdefer backCache.Close()\n\tcache := xcache.NewMulti(frontCache, backCache)\n\n\tctx := context.Background()\n\tkey := \"example-multi\"\n\tvalue := []byte(\"Hello Multi Cache\")\n\tttl := 10 * time.Minute\n\n\t// save a key for 10 minutes\n\tif err := cache.Save(ctx, key, value, ttl); err != nil {\n\t\tfmt.Println(\"could not save Multi cache key: \" + err.Error())\n\t}\n\n\t// load the key's value\n\tif value, err := cache.Load(ctx, key); err != nil {\n\t\tfmt.Println(\"could not get Multi cache key: \" + err.Error())\n\t} else {\n\t\tfmt.Println(string(value))\n\t}\n\n\t// should output:\n\t// Hello Multi Cache\n}\n```\n\n\n### Reconfiguring on the fly the caches\nIf you need to change caches' configs without redeploying your application, you can use the [xconf](https://github.com/actforgood/xconf) pkg adapter to initialize the caches: `NewMemoryWithConfig` / `NewRedis6WithConfig` / `NewRedis7WithConfig`.\n\n\n### Monitoring your cache stats\nIf you need to monitor your cache's statistics, you can check `StatsWatcher` which can help you in this matter. It executes periodically a provided callback upon cache's `Stats`, thus, you can log them / sent them to a metrics system.\n\n\n### Running tests / benchmarks\nin `scripts` folder there is a shell script that sets up a Redis docker based environment with desired configuration and runs integration tests / benchmarks.\n```bash\ncd /path/to/xcache\n./scripts/run_local.sh cluster  // example of running tests in Redis cluster setup\n./scripts/run_local.sh single bench // example of running benchmarks in Redis single instance setup.\n```\n\n### TODOs:\nThings that can be added to pkg, extended:  \n\n- Support also Memcached.\n\n### License\nThis package is released under a MIT license. See [LICENSE](LICENSE).  \nOther 3rd party packages directly used by this package are released under their own licenses.  \n\n* github.com/coocood/freecache - [MIT License](https://github.com/coocood/freecache/blob/master/LICENSE)  \n* github.com/redis/go-redis/v8 - [BSD (2 Clause) License](https://github.com/redis/go-redis/blob/v8.11.5/LICENSE)\n* github.com/redis/go-redis/v9 - [BSD (2 Clause) License](https://github.com/redis/go-redis/blob/v9.0.0-beta.2/LICENSE)    \n* github.com/actforgood/xerr - [MIT License](https://github.com/actforgood/xerr/blob/main/LICENSE)  \n* github.com/actforgood/xlog - [MIT License](https://github.com/actforgood/xlog/blob/main/LICENSE)  \n* github.com/actforgood/xconf - [MIT License](https://github.com/actforgood/xconf/blob/main/LICENSE)  \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Factforgood%2Fxcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Factforgood%2Fxcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Factforgood%2Fxcache/lists"}