├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── changelog.yml ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── go.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_cn.md ├── badger └── badger.go ├── boltdb └── boltdb.go ├── buntdb └── buntdb.go ├── cache.go ├── driver.go ├── driver_file.go ├── driver_memory.go ├── driver_test.go ├── example_test.go ├── gcache ├── gcache.go └── gcache_test.go ├── go.mod ├── go.sum ├── gocache ├── gocache.go └── gocache_test.go ├── goredis ├── goredis.go └── goredis_test.go ├── helper.go ├── leveldb └── leveldb.go ├── manager.go ├── memcached ├── memcached.go └── memcached_test.go ├── nutsdb └── nutsdb.go ├── redis ├── redigo.go └── redigo_test.go └── testdata └── .keep /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report about: Create a report to help us improve title: '' 3 | labels: '' 4 | assignees: inhere 5 | 6 | --- 7 | 8 | **System (please complete the following information):** 9 | 10 | - OS: `linux` [e.g. linux, macOS] 11 | - GO Version: `1.13` [e.g. `1.13`] 12 | - Pkg Version: `1.1.1` [e.g. `1.1.1`] 13 | 14 | **Describe the bug** 15 | 16 | A clear and concise description of what the bug is. 17 | 18 | **To Reproduce** 19 | 20 | ```go 21 | // go code 22 | ``` 23 | 24 | **Expected behavior** 25 | 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | 30 | If applicable, add screenshots to help explain your problem. 31 | 32 | **Additional context** 33 | 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/changelog.yml: -------------------------------------------------------------------------------- 1 | title: '## Change Log' 2 | # style allow: simple, markdown(mkdown), ghr(gh-release) 3 | style: gh-release 4 | # group names 5 | names: [Refactor, Fixed, Feature, Update, Other] 6 | #repo_url: https://github.com/gookit/config 7 | 8 | filters: 9 | # message length should >= 12 10 | - name: msg_len 11 | min_len: 12 12 | # message words should >= 3 13 | - name: words_len 14 | min_len: 3 15 | - name: keyword 16 | keyword: format code 17 | exclude: true 18 | - name: keywords 19 | keywords: format code, action test 20 | exclude: true 21 | 22 | # group match rules 23 | # not matched will use 'Other' group. 24 | rules: 25 | - name: Refactor 26 | start_withs: [refactor, break] 27 | contains: ['refactor:'] 28 | - name: Fixed 29 | start_withs: [fix] 30 | contains: ['fix:'] 31 | - name: Feature 32 | start_withs: [feat, new] 33 | contains: [feature] 34 | - name: Update 35 | start_withs: [update] 36 | contains: ['update:', 'up:'] 37 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: gomod 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | open-pull-requests-limit: 10 9 | 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | # Check for updates to GitHub Actions every weekday 14 | interval: "daily" 15 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '27 22 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'go' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v4 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v3 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v3 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v3 73 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Unit-Tests 2 | on: 3 | push: 4 | paths: 5 | - 'go.mod' 6 | - '**.go' 7 | - '**.yml' 8 | 9 | jobs: 10 | 11 | test: 12 | name: Test on go ${{ matrix.go_version }} and ${{ matrix.os }} 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | go_version: [ 1.23, 1.22, 1.21, 1.19, '1.20' ] 17 | os: [ ubuntu-latest ] # , macOS-latest, windows-latest 18 | 19 | services: 20 | # https://docs.github.com/en/actions/guides/creating-redis-service-containers 21 | redis: 22 | image: redis 23 | ports: 24 | - 6379:6379 # export 6379 the port 25 | options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 26 | 27 | steps: 28 | - name: Check out code 29 | uses: actions/checkout@v4 30 | 31 | - name: Setup Go Faster 32 | uses: WillAbides/setup-go-faster@v1.14.0 33 | timeout-minutes: 3 34 | with: 35 | go-version: ${{ matrix.go_version }} 36 | 37 | - name: Revive check 38 | uses: morphy2k/revive-action@v2.7.7 39 | if: ${{ matrix.os == 'ubuntu-latest' }} 40 | with: 41 | # Exclude patterns, separated by semicolons (optional) 42 | exclude: "./internal/..." 43 | 44 | - name: Run staticcheck 45 | uses: reviewdog/action-staticcheck@v1 46 | with: 47 | github_token: ${{ secrets.github_token }} 48 | # Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review]. 49 | reporter: github-pr-review 50 | # Report all results. 51 | filter_mode: nofilter 52 | # Exit with 1 when it find at least one finding. 53 | fail_on_error: true 54 | 55 | - name: Run unit tests 56 | run: | 57 | go mod tidy 58 | go test -v -cover ./... 59 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Tag-release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release: 10 | name: Release new version 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 10 13 | strategy: 14 | fail-fast: true 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Setup ENV 23 | # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable 24 | run: | 25 | echo "RELEASE_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV 26 | echo "RELEASE_NAME=$GITHUB_WORKFLOW" >> $GITHUB_ENV 27 | 28 | - name: Generate changelog 29 | run: | 30 | curl https://github.com/gookit/gitw/releases/latest/download/chlog-linux-amd64 -L -o /usr/local/bin/chlog 31 | chmod a+x /usr/local/bin/chlog 32 | chlog -c .github/changelog.yml -o changelog.md prev last 33 | 34 | # https://github.com/softprops/action-gh-release 35 | - name: Create release and upload assets 36 | uses: softprops/action-gh-release@v2 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | name: ${{ env.RELEASE_TAG }} 41 | tag_name: ${{ env.RELEASE_TAG }} 42 | body_path: changelog.md 43 | token: ${{ secrets.GITHUB_TOKEN }} 44 | # files: macos-chlog.exe 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | *.iws 4 | 5 | .DS_Store 6 | 7 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 8 | *.o 9 | *.a 10 | *.so 11 | 12 | # Folders 13 | _obj 14 | _test 15 | release 16 | debian 17 | bin 18 | 19 | # Architecture specific extensions/prefixes 20 | *.[568vq] 21 | [568vq].out 22 | 23 | *.cgo1.go 24 | *.cgo2.c 25 | _cgo_defun.c 26 | _cgo_gotypes.go 27 | _cgo_export.* 28 | 29 | _testmain.go 30 | 31 | *.swp 32 | *.swo 33 | 34 | *.exe 35 | *.test 36 | 37 | *.bak 38 | 39 | cmd/gost/gost 40 | snap 41 | 42 | local*.sh 43 | local*.cmd 44 | /testdata/ 45 | oryxBuildBinary -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 inhere 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cache 2 | 3 | ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/cache?style=flat-square) 4 | [![GoDoc](https://godoc.org/github.com/gookit/cache?status.svg)](https://pkg.go.dev/github.com/gookit/cache) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/cache)](https://goreportcard.com/report/github.com/gookit/cache) 6 | [![Actions Status](https://github.com/gookit/cache/workflows/Unit-Tests/badge.svg)](https://github.com/gookit/cache/actions) 7 | 8 | > **[中文说明](README_cn.md)** 9 | 10 | Generic cache use and cache manager for golang. 11 | 12 | Provide a unified usage API by packaging various commonly used drivers. 13 | 14 | > All cache driver implemented the `cache.Cache` interface. So, You can add any custom driver. 15 | 16 | **Packaged Drivers:** 17 | 18 | - `goredis` https://github.com/go-redis/redis 19 | - `redis` https://github.com/gomodule/redigo 20 | - `memcached` https://github.com/bradfitz/gomemcache 21 | - `buntdb` https://github.com/tidwall/buntdb 22 | - `boltdb` https://github.com/etcd-io/bbolt 23 | - `badger` https://github.com/dgraph-io/badger 24 | - `nutsdb` https://github.com/xujiajun/nutsdb 25 | - `goleveldb` https://github.com/syndtr/goleveldb 26 | - `gcache` https://github.com/bluele/gcache 27 | - `gocache` https://github.com/patrickmn/go-cache 28 | - `bigcache` https://github.com/allegro/bigcache 29 | 30 | Internal: 31 | 32 | - file internal driver [driver_file.go](driver_file.go) 33 | - memory internal driver [driver_memory.go](driver_memory.go) 34 | 35 | > Notice: The built-in implementation is relatively simple and is not recommended for production environments; 36 | > the production environment recommends using the third-party drivers listed above. 37 | 38 | ## GoDoc 39 | 40 | - [doc on gowalker](https://gowalker.org/github.com/gookit/cache) 41 | - [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/cache.v1) 42 | - [godoc for github](https://pkg.go.dev/github.com/gookit/cache) 43 | 44 | ## Install 45 | 46 | The package supports 3 last Go versions and requires a Go version with modules support. 47 | 48 | ```bash 49 | go get github.com/gookit/cache 50 | ``` 51 | 52 | ## Cache Interface 53 | 54 | All cache driver implemented the `cache.Cache` interface. So, You can add any custom driver. 55 | 56 | ```go 57 | type Cache interface { 58 | // basic operation 59 | Has(key string) bool 60 | Get(key string) any 61 | Set(key string, val any, ttl time.Duration) (err error) 62 | Del(key string) error 63 | // multi operation 64 | GetMulti(keys []string) map[string]any 65 | SetMulti(values map[string]any, ttl time.Duration) (err error) 66 | DelMulti(keys []string) error 67 | // clear and close 68 | Clear() error 69 | Close() error 70 | } 71 | ``` 72 | 73 | ## Usage 74 | 75 | ```go 76 | package main 77 | 78 | import ( 79 | "fmt" 80 | 81 | "github.com/gookit/cache" 82 | "github.com/gookit/cache/gcache" 83 | "github.com/gookit/cache/gocache" 84 | "github.com/gookit/cache/goredis" 85 | "github.com/gookit/cache/redis" 86 | ) 87 | 88 | func main() { 89 | // register one(or some) cache driver 90 | cache.Register(cache.DvrFile, cache.NewFileCache("")) 91 | // cache.Register(cache.DvrMemory, cache.NewMemoryCache()) 92 | cache.Register(gcache.Name, gcache.New(1000)) 93 | cache.Register(gocache.Name, gocache.NewGoCache(cache.OneDay, cache.FiveMinutes)) 94 | cache.Register(redis.Name, redis.Connect("127.0.0.1:6379", "", 0)) 95 | cache.Register(goredis.Name, goredis.Connect("127.0.0.1:6379", "", 0)) 96 | 97 | // setting default driver name 98 | cache.DefaultUse(gocache.Name) 99 | 100 | // quick use.(it is default driver) 101 | // 102 | // set 103 | cache.Set("name", "cache value", cache.TwoMinutes) 104 | // get 105 | val := cache.Get("name") 106 | // del 107 | cache.Del("name") 108 | 109 | // get: "cache value" 110 | fmt.Print(val) 111 | 112 | // More ... 113 | // fc := cache.Driver(gcache.Name) 114 | // fc.Set("key", "value", 10) 115 | // fc.Get("key") 116 | } 117 | ``` 118 | 119 | ## With Options 120 | 121 | ```go 122 | gords := goredis.Connect("127.0.0.1:6379", "", 0) 123 | gords.WithOptions(cache.WithPrefix("cache_"), cache.WithEncode(true)) 124 | 125 | cache.Register(goredis.Name, gords) 126 | 127 | // set 128 | // real key is: "cache_name" 129 | cache.Set("name", "cache value", cache.TwoMinutes) 130 | 131 | // get: "cache value" 132 | val := cache.Get("name") 133 | ``` 134 | 135 | ## Gookit packages 136 | 137 | - [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files 138 | - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP 139 | - [gookit/gcli](https://github.com/gookit/gcli) build CLI application, tool library, running CLI commands 140 | - [gookit/slog](https://github.com/gookit/slog) Lightweight, extensible, configurable logging library written in Go 141 | - [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go 142 | - [gookit/cache](https://github.com/gookit/cache) Provide a unified usage API by packaging various commonly used drivers. 143 | - [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags 144 | - [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support 145 | - [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data 146 | - [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data 147 | - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more 148 | - More, please see https://github.com/gookit 149 | 150 | ## License 151 | 152 | **MIT** 153 | -------------------------------------------------------------------------------- /README_cn.md: -------------------------------------------------------------------------------- 1 | # Cache 2 | 3 | ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/cache?style=flat-square) 4 | [![GoDoc](https://godoc.org/github.com/gookit/cache?status.svg)](https://pkg.go.dev/github.com/gookit/cache) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/cache)](https://goreportcard.com/report/github.com/gookit/cache) 6 | [![Actions Status](https://github.com/gookit/cache/workflows/Unit-Tests/badge.svg)](https://github.com/gookit/cache/actions) 7 | 8 | > **[EN README](README.md)** 9 | 10 | Golang 通用的缓存管理使用库。 11 | 12 | 通过包装各种常用的驱动,屏蔽掉底层各个驱动的不同使用方法,来提供统一的使用API。 13 | 14 | > 所有缓存驱动程序都实现了 `cache.Cache` 接口。 因此,您可以添加任何自定义驱动程序。 15 | 16 | **已经封装的驱动:** 17 | 18 | - `goredis` https://github.com/go-redis/redis 19 | - `redis` https://github.com/gomodule/redigo 20 | - `memcached` https://github.com/bradfitz/gomemcache 21 | - `buntdb` https://github.com/tidwall/buntdb 22 | - `boltdb` https://github.com/etcd-io/bbolt 23 | - `badger` https://github.com/dgraph-io/badger 24 | - `nutsdb` https://github.com/xujiajun/nutsdb 25 | - `goleveldb` https://github.com/syndtr/goleveldb 26 | - `gcache` https://github.com/bluele/gcache 27 | - `gocache` https://github.com/patrickmn/go-cache 28 | - `bigcache` https://github.com/allegro/bigcache 29 | 30 | **内置实现:** 31 | 32 | - file 简单的文件缓存(_当前包的内置实现_) 33 | - memory 简单的内存缓存(_当前包的内置实现_) 34 | 35 | > 注意:内置实现比较简单,不推荐生产环境使用;生产环境推荐使用上面列出的三方驱动。 36 | 37 | ## GoDoc 38 | 39 | - [doc on gowalker](https://gowalker.org/github.com/gookit/cache) 40 | - [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/cache.v1) 41 | - [godoc for github](https://pkg.go.dev/github.com/gookit/cache) 42 | 43 | ## 安装 44 | 45 | ```bash 46 | go get github.com/gookit/cache 47 | ``` 48 | 49 | ## 接口方法 50 | 51 | 所有缓存驱动程序都实现了 `cache.Cache` 接口。 因此,您可以添加任何自定义驱动程序。 52 | 53 | ```go 54 | // Cache interface definition 55 | type Cache interface { 56 | // basic op 57 | Has(key string) bool 58 | Get(key string) any 59 | Set(key string, val any, ttl time.Duration) (err error) 60 | Del(key string) error 61 | // multi op 62 | GetMulti(keys []string) map[string]any 63 | SetMulti(values map[string]any, ttl time.Duration) (err error) 64 | DelMulti(keys []string) error 65 | // clear & close 66 | Clear() error 67 | Close() error 68 | } 69 | ``` 70 | 71 | ## 使用 72 | 73 | ```go 74 | package main 75 | 76 | import ( 77 | "fmt" 78 | 79 | "github.com/gookit/cache" 80 | "github.com/gookit/cache/gcache" 81 | "github.com/gookit/cache/gocache" 82 | "github.com/gookit/cache/goredis" 83 | "github.com/gookit/cache/redis" 84 | ) 85 | 86 | func main() { 87 | // 注册一个(或多个)缓存驱动 88 | cache.Register(cache.DvrFile, cache.NewFileCache("")) 89 | // cache.Register(cache.DvrMemory, cache.NewMemoryCache()) 90 | cache.Register(gcache.Name, gcache.New(1000)) 91 | cache.Register(gocache.Name, gocache.NewGoCache(cache.OneDay, cache.FiveMinutes)) 92 | cache.Register(redis.Name, redis.Connect("127.0.0.1:6379", "", 0)) 93 | cache.Register(goredis.Name, goredis.Connect("127.0.0.1:6379", "", 0)) 94 | 95 | // 设置默认驱动名称 96 | cache.DefaultUse(goredis.Name) 97 | 98 | // 快速使用(默认驱动) 99 | // 100 | // set 101 | cache.Set("name", "cache value", cache.TwoMinutes) 102 | // get 103 | val := cache.Get("name") 104 | // del 105 | cache.Del("name") 106 | 107 | // Out: "cache value" 108 | fmt.Print(val) 109 | 110 | // 使用已注册的其他驱动 111 | client := cache.Driver(gcache.Name) 112 | client.Set("key", "val", cache.Seconds3) 113 | val = client.Get("key") 114 | // Out: "val" 115 | fmt.Print(val) 116 | } 117 | ``` 118 | 119 | ## 设置选项 120 | 121 | ```go 122 | gords := goredis.Connect("127.0.0.1:6379", "", 0) 123 | gords.WithOptions(cache.WithPrefix("cache_"), cache.WithEncode(true)) 124 | 125 | cache.Register(goredis.Name, gords) 126 | 127 | // set 128 | // real key is: "cache_name" 129 | cache.Set("name", "cache value", cache.TwoMinutes) 130 | 131 | // get: "cache value" 132 | val := cache.Get("name") 133 | ``` 134 | 135 | ## Gookit packages 136 | 137 | - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP 138 | - [gookit/gcli](https://github.com/gookit/gcli) build CLI application, tool library, running CLI commands 139 | - [gookit/slog](https://github.com/gookit/slog) Lightweight, extensible, configurable logging library written in Go 140 | - [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go 141 | - [gookit/cache](https://github.com/gookit/cache) Provide a unified usage API by packaging various commonly used drivers. 142 | - [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags 143 | - [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support 144 | - [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data 145 | - [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data 146 | - [gookit/ini](https://github.com/gookit/ini) INI parse, simple go config management, use INI files 147 | - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more 148 | - More, please see https://github.com/gookit 149 | 150 | ## License 151 | 152 | **[MIT](LICENSE)** 153 | -------------------------------------------------------------------------------- /badger/badger.go: -------------------------------------------------------------------------------- 1 | // Package badger use the https://github.com/dgraph-io/badger as cache driver 2 | package badger 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/dgraph-io/badger" 8 | ) 9 | 10 | // Name driver name 11 | const Name = "badger" 12 | 13 | // BadgerDB definition 14 | type BadgerDB struct { 15 | db *badger.DB 16 | } 17 | 18 | func (c *BadgerDB) Has(key string) bool { 19 | panic("implement me") 20 | } 21 | 22 | func (c *BadgerDB) Get(key string) any { 23 | panic("implement me") 24 | } 25 | 26 | func (c *BadgerDB) Set(key string, val any, ttl time.Duration) (err error) { 27 | panic("implement me") 28 | } 29 | 30 | func (c *BadgerDB) Del(key string) error { 31 | panic("implement me") 32 | } 33 | 34 | func (c *BadgerDB) GetMulti(keys []string) map[string]any { 35 | panic("implement me") 36 | } 37 | 38 | func (c *BadgerDB) SetMulti(values map[string]any, ttl time.Duration) (err error) { 39 | panic("implement me") 40 | } 41 | 42 | func (c *BadgerDB) DelMulti(keys []string) error { 43 | panic("implement me") 44 | } 45 | 46 | func (c *BadgerDB) Clear() error { 47 | panic("implement me") 48 | } 49 | 50 | func (c *BadgerDB) Close() error { 51 | return c.db.Close() 52 | } 53 | -------------------------------------------------------------------------------- /boltdb/boltdb.go: -------------------------------------------------------------------------------- 1 | // Package bolt use the go.etcd.io/bbolt(github.com/etcd-io/bbolt) as cache driver 2 | package boltdb 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/gookit/cache" 8 | "go.etcd.io/bbolt" 9 | ) 10 | 11 | // Name driver name 12 | const Name = "boltDB" 13 | 14 | // BoltDB definition 15 | type BoltDB struct { 16 | cache.BaseDriver 17 | // db file path. eg "path/to/my.db" 18 | file string 19 | // db instance 20 | db *bbolt.DB 21 | // Bucket name 22 | Bucket string 23 | } 24 | 25 | // New a BuntDB instance 26 | func New(file string) *BoltDB { 27 | db, err := bbolt.Open(file, 0666, nil) 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | return &BoltDB{db: db, Bucket: "myBucket"} 33 | } 34 | 35 | // Has value check by key 36 | func (c *BoltDB) Has(key string) bool { 37 | return c.Get(key) != nil 38 | } 39 | 40 | // Get value by key 41 | func (c *BoltDB) Get(key string) any { 42 | var val any 43 | err := c.db.View(func(tx *bbolt.Tx) error { 44 | b := tx.Bucket([]byte(c.Bucket)) 45 | bs := b.Get([]byte(key)) 46 | 47 | if err := c.UnmarshalTo(bs, &val); err != nil { 48 | return err 49 | } 50 | 51 | return nil 52 | }) 53 | 54 | if err != nil { 55 | return nil 56 | } 57 | return val 58 | } 59 | 60 | // Set value by key 61 | func (c *BoltDB) Set(key string, val any, _ time.Duration) (err error) { 62 | bts, err := c.MustMarshal(val) 63 | if err != nil { 64 | return 65 | } 66 | 67 | return c.db.Update(func(tx *bbolt.Tx) error { 68 | b := tx.Bucket([]byte(c.Bucket)) 69 | err := b.Put([]byte(key), bts) 70 | return err 71 | }) 72 | } 73 | 74 | // Del value by key 75 | func (c *BoltDB) Del(key string) error { 76 | panic("implement me") 77 | } 78 | 79 | // GetMulti values by multi key 80 | func (c *BoltDB) GetMulti(keys []string) map[string]any { 81 | panic("implement me") 82 | } 83 | 84 | // SetMulti values by multi key 85 | func (c *BoltDB) SetMulti(values map[string]any, ttl time.Duration) (err error) { 86 | panic("implement me") 87 | } 88 | 89 | // DelMulti values by multi key 90 | func (c *BoltDB) DelMulti(keys []string) error { 91 | panic("implement me") 92 | } 93 | 94 | // Clear all data 95 | func (c *BoltDB) Clear() error { 96 | panic("implement me") 97 | } 98 | 99 | // Close db 100 | func (c *BoltDB) Close() error { 101 | err := c.db.Sync() 102 | if err != nil { 103 | return err 104 | } 105 | 106 | // do close 107 | return c.db.Close() 108 | } 109 | -------------------------------------------------------------------------------- /buntdb/buntdb.go: -------------------------------------------------------------------------------- 1 | // Package buntdb use the github.com/tidwall/buntdb as cache driver 2 | package buntdb 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/gookit/cache" 8 | "github.com/tidwall/buntdb" 9 | ) 10 | 11 | // Name driver name 12 | const Name = "buntDB" 13 | 14 | // Memory open a file that does not persist to disk. 15 | const Memory = ":memory:" 16 | 17 | // BuntDB definition. 18 | type BuntDB struct { 19 | cache.BaseDriver 20 | // db file path. eg "path/to/my.db" 21 | file string 22 | // db instance 23 | db *buntdb.DB 24 | } 25 | 26 | // NewMemory new a memory db 27 | func NewMemory() *BuntDB { 28 | return New(Memory) 29 | } 30 | 31 | // New a BuntDB instance 32 | func New(file string) *BuntDB { 33 | if file == "" { // use memory 34 | file = Memory 35 | } 36 | 37 | db, err := buntdb.Open(file) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | return &BuntDB{ 43 | db: db, 44 | } 45 | } 46 | 47 | // Db get 48 | func (c *BuntDB) Db() *buntdb.DB { 49 | return c.db 50 | } 51 | 52 | // Has key 53 | func (c *BuntDB) Has(key string) bool { 54 | has := false 55 | err := c.db.View(func(tx *buntdb.Tx) error { 56 | val, err := tx.Get(key, false) 57 | has = val != "" 58 | return err 59 | }) 60 | 61 | if err != nil { 62 | has = false 63 | } 64 | 65 | return has 66 | } 67 | 68 | // Get value by key 69 | func (c *BuntDB) Get(key string) any { 70 | var val any 71 | err := c.db.View(func(tx *buntdb.Tx) error { 72 | str, err := tx.Get(key, false) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | return c.UnmarshalTo([]byte(str), &val) 78 | }) 79 | 80 | if err != nil { 81 | return nil 82 | } 83 | return val 84 | } 85 | 86 | // Set value by key 87 | func (c *BuntDB) Set(key string, val any, ttl time.Duration) (err error) { 88 | bts, err := c.MustMarshal(val) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | return c.db.Update(func(tx *buntdb.Tx) (err error) { 94 | opt := &buntdb.SetOptions{} 95 | if ttl > 0 { 96 | opt.TTL = ttl 97 | opt.Expires = true 98 | } 99 | 100 | _, _, err = tx.Set(key, string(bts), opt) 101 | return err 102 | }) 103 | } 104 | 105 | // Del value by key 106 | func (c *BuntDB) Del(key string) error { 107 | return c.db.Update(func(tx *buntdb.Tx) error { 108 | _, err := tx.Delete(key) 109 | return err 110 | }) 111 | } 112 | 113 | // GetMulti values by multi key 114 | func (c *BuntDB) GetMulti(keys []string) map[string]any { 115 | results := make(map[string]any, len(keys)) 116 | err := c.db.View(func(tx *buntdb.Tx) error { 117 | for _, key := range keys { 118 | str, err := tx.Get(key, false) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | var val any 124 | err = c.UnmarshalTo([]byte(str), &val) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | results[key] = val 130 | } 131 | return nil 132 | }) 133 | 134 | if err != nil { 135 | return nil 136 | } 137 | 138 | return results 139 | } 140 | 141 | // SetMulti values by multi key 142 | func (c *BuntDB) SetMulti(values map[string]any, ttl time.Duration) (err error) { 143 | return c.db.Update(func(tx *buntdb.Tx) (err error) { 144 | opt := &buntdb.SetOptions{} 145 | if ttl > 0 { 146 | opt.TTL = ttl 147 | opt.Expires = true 148 | } 149 | 150 | for key, val := range values { 151 | bts, err := c.MustMarshal(val) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | _, _, err = tx.Set(key, string(bts), opt) 157 | if err != nil { 158 | return err 159 | } 160 | } 161 | 162 | return nil 163 | }) 164 | } 165 | 166 | // DelMulti values by multi key 167 | func (c *BuntDB) DelMulti(keys []string) error { 168 | return c.db.Update(func(tx *buntdb.Tx) (err error) { 169 | for _, k := range keys { 170 | if _, err = tx.Delete(k); err != nil { 171 | return err 172 | } 173 | } 174 | 175 | return nil 176 | }) 177 | } 178 | 179 | // Clear all cache data 180 | func (c *BuntDB) Clear() error { 181 | return c.db.Update(func(tx *buntdb.Tx) error { 182 | return tx.DeleteAll() 183 | }) 184 | } 185 | 186 | // Close cache db 187 | func (c *BuntDB) Close() error { 188 | return c.db.Close() 189 | } 190 | -------------------------------------------------------------------------------- /cache.go: -------------------------------------------------------------------------------- 1 | // Package cache is a generic cache use and cache manager for golang. 2 | // FileCache is a simple local file system cache implement. 3 | // MemoryCache is a simple memory cache implement. 4 | package cache 5 | 6 | import ( 7 | "time" 8 | 9 | "github.com/gookit/gsr" 10 | ) 11 | 12 | // Cache interface definition 13 | type Cache = gsr.SimpleCacher 14 | 15 | // some generic expire time define. 16 | const ( 17 | // Forever Always exist 18 | Forever = 0 19 | 20 | // Seconds1 1 second 21 | Seconds1 = time.Second 22 | // Seconds2 2 second 23 | Seconds2 = 2 * time.Second 24 | // Seconds3 3 second 25 | Seconds3 = 3 * time.Second 26 | // Seconds5 5 second 27 | Seconds5 = 5 * time.Second 28 | // Seconds6 6 second 29 | Seconds6 = 6 * time.Second 30 | // Seconds7 7 second 31 | Seconds7 = 7 * time.Second 32 | // Seconds8 8 second 33 | Seconds8 = 8 * time.Second 34 | // Seconds9 9 second 35 | Seconds9 = 9 * time.Second 36 | // Seconds10 10 second 37 | Seconds10 = 10 * time.Second 38 | // Seconds15 15 second 39 | Seconds15 = 15 * time.Second 40 | // Seconds20 20 second 41 | Seconds20 = 20 * time.Second 42 | // Seconds30 30 second 43 | Seconds30 = 30 * time.Second 44 | 45 | // OneMinutes 1 minutes 46 | OneMinutes = 60 * time.Second 47 | // TwoMinutes 2 minutes 48 | TwoMinutes = 120 * time.Second 49 | // ThreeMinutes 3 minutes 50 | ThreeMinutes = 180 * time.Second 51 | // FiveMinutes 5 minutes 52 | FiveMinutes = 300 * time.Second 53 | // TenMinutes 10 minutes 54 | TenMinutes = 600 * time.Second 55 | // FifteenMinutes 15 minutes 56 | FifteenMinutes = 900 * time.Second 57 | // HalfHour half an hour 58 | HalfHour = 1800 * time.Second 59 | // OneHour 1 hour 60 | OneHour = 3600 * time.Second 61 | // TwoHour 2 hours 62 | TwoHour = 7200 * time.Second 63 | // ThreeHour 3 hours 64 | ThreeHour = 10800 * time.Second 65 | // HalfDay 12 hours(half of the day) 66 | HalfDay = 43200 * time.Second 67 | // OneDay 24 hours(1 day) 68 | OneDay = 86400 * time.Second 69 | // TwoDay 2 day 70 | TwoDay = 172800 * time.Second 71 | // ThreeDay 3 day 72 | ThreeDay = 259200 * time.Second 73 | // OneWeek 7 day(one week) 74 | OneWeek = 604800 * time.Second 75 | ) 76 | 77 | /************************************************************* 78 | * config default cache manager 79 | *************************************************************/ 80 | 81 | // default cache driver manager instance 82 | var std = NewManager() 83 | 84 | // Register driver to manager instance 85 | func Register(name string, driver Cache) *Manager { 86 | std.Register(name, driver) 87 | return std 88 | } 89 | 90 | // Unregister an cache driver 91 | func Unregister(name string) int { 92 | return std.Unregister(name) 93 | } 94 | 95 | // UnregisterAll cache drivers 96 | func UnregisterAll(fn ...func(cache Cache)) int { 97 | return std.UnregisterAll(fn...) 98 | } 99 | 100 | // SetDefName set default driver name. 101 | // Deprecated 102 | // 103 | // please use DefaultUse() instead it 104 | func SetDefName(driverName string) { 105 | std.DefaultUse(driverName) 106 | } 107 | 108 | // DefaultUse set default driver name 109 | func DefaultUse(driverName string) { 110 | std.DefaultUse(driverName) 111 | } 112 | 113 | // Use driver object by name and set it as default driver. 114 | func Use(driverName string) Cache { 115 | return std.Use(driverName) 116 | } 117 | 118 | // GetCache returns a driver instance by name. alias of Driver() 119 | func GetCache(driverName string) Cache { 120 | return std.Cache(driverName) 121 | } 122 | 123 | // Driver get a driver instance by name 124 | func Driver(driverName string) Cache { 125 | return std.Driver(driverName) 126 | } 127 | 128 | // Std get default cache manager instance 129 | func Std() *Manager { 130 | return std 131 | } 132 | 133 | // DefManager get default cache manager instance 134 | func DefManager() *Manager { 135 | return std 136 | } 137 | 138 | // Default get default cache driver instance 139 | func Default() Cache { 140 | return std.Default() 141 | } 142 | 143 | // Close all drivers 144 | func Close() error { 145 | return std.Close() 146 | } 147 | 148 | // ClearAll all drivers caches 149 | func ClearAll() error { 150 | return std.ClearAll() 151 | } 152 | 153 | /************************************************************* 154 | * quick use by default cache driver 155 | *************************************************************/ 156 | 157 | // Has cache key 158 | func Has(key string) bool { 159 | return std.Default().Has(key) 160 | } 161 | 162 | // Get value by key 163 | func Get(key string) any { 164 | return std.Default().Get(key) 165 | } 166 | 167 | // Set value by key 168 | func Set(key string, val any, ttl time.Duration) error { 169 | return std.Default().Set(key, val, ttl) 170 | } 171 | 172 | // Del value by key 173 | func Del(key string) error { 174 | return std.Default().Del(key) 175 | } 176 | 177 | // GetMulti values by keys 178 | func GetMulti(keys []string) map[string]any { 179 | return std.Default().GetMulti(keys) 180 | } 181 | 182 | // SetMulti values 183 | func SetMulti(mv map[string]any, ttl time.Duration) error { 184 | return std.Default().SetMulti(mv, ttl) 185 | } 186 | 187 | // DelMulti values by keys 188 | func DelMulti(keys []string) error { 189 | return std.Default().DelMulti(keys) 190 | } 191 | 192 | // Clear all caches 193 | func Clear() error { 194 | return std.Default().Clear() 195 | } 196 | -------------------------------------------------------------------------------- /driver.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | 7 | "github.com/gookit/gsr" 8 | ) 9 | 10 | type ( 11 | // MarshalFunc define 12 | MarshalFunc func(v any) ([]byte, error) 13 | 14 | // UnmarshalFunc define 15 | UnmarshalFunc func(data []byte, v any) error 16 | ) 17 | 18 | // data (Un)marshal func 19 | var ( 20 | Marshal MarshalFunc = json.Marshal 21 | Unmarshal UnmarshalFunc = json.Unmarshal 22 | 23 | errNoMarshal = errors.New("must set Marshal func") 24 | errNoUnmarshal = errors.New("must set Unmarshal func") 25 | ) 26 | 27 | // Option struct 28 | type Option struct { 29 | Debug bool 30 | // Encode (Un)marshal save data 31 | Encode bool 32 | Logger gsr.Printer 33 | // Prefix key prefix 34 | Prefix string 35 | } 36 | 37 | /************************************************************* 38 | * base driver 39 | *************************************************************/ 40 | 41 | // BaseDriver struct 42 | type BaseDriver struct { 43 | opt Option 44 | // last error 45 | lastErr error 46 | } 47 | 48 | // WithDebug add option: debug 49 | func WithDebug(debug bool) func(opt *Option) { 50 | return func(opt *Option) { 51 | opt.Debug = debug 52 | } 53 | } 54 | 55 | // WithEncode add option: encode 56 | func WithEncode(encode bool) func(opt *Option) { 57 | return func(opt *Option) { 58 | opt.Encode = encode 59 | } 60 | } 61 | 62 | // WithPrefix add option: prefix 63 | func WithPrefix(prefix string) func(opt *Option) { 64 | return func(opt *Option) { 65 | opt.Prefix = prefix 66 | } 67 | } 68 | 69 | // WithOptions for driver 70 | func (l *BaseDriver) WithOptions(optFns ...func(option *Option)) { 71 | for _, optFn := range optFns { 72 | optFn(&l.opt) 73 | } 74 | } 75 | 76 | // MustMarshal cache value 77 | func (l *BaseDriver) MustMarshal(val any) ([]byte, error) { 78 | if Marshal == nil { 79 | return nil, errNoMarshal 80 | } 81 | return Marshal(val) 82 | } 83 | 84 | // Marshal cache value 85 | func (l *BaseDriver) Marshal(val any) (any, error) { 86 | if l.opt.Encode && Marshal != nil { 87 | return Marshal(val) 88 | } 89 | 90 | return val, nil 91 | } 92 | 93 | // UnmarshalTo cache value 94 | func (l *BaseDriver) UnmarshalTo(bts []byte, ptr any) error { 95 | if Unmarshal == nil { 96 | return errNoUnmarshal 97 | } 98 | return Unmarshal(bts, ptr) 99 | } 100 | 101 | // Unmarshal cache value 102 | func (l *BaseDriver) Unmarshal(val []byte, err error) any { 103 | if err != nil { 104 | l.SetLastErr(err) 105 | return nil 106 | } 107 | 108 | var newV any 109 | if l.opt.Encode && Unmarshal != nil { 110 | err := Unmarshal(val, &newV) 111 | l.SetLastErr(err) 112 | return newV 113 | } 114 | 115 | return val 116 | } 117 | 118 | // Key real cache key build 119 | func (l *BaseDriver) Key(key string) string { 120 | if l.opt.Prefix != "" { 121 | return l.opt.Prefix + key 122 | } 123 | return key 124 | } 125 | 126 | // BuildKeys real cache keys build 127 | func (l *BaseDriver) BuildKeys(keys []string) []string { 128 | if l.opt.Prefix == "" { 129 | return keys 130 | } 131 | 132 | rks := make([]string, 0, len(keys)) 133 | 134 | for _, key := range keys { 135 | rks = append(rks, l.opt.Prefix+key) 136 | } 137 | return rks 138 | } 139 | 140 | // Debugf print an debug message 141 | func (l *BaseDriver) Debugf(format string, v ...any) { 142 | if l.opt.Debug && l.opt.Logger != nil { 143 | l.opt.Logger.Printf(format, v...) 144 | } 145 | } 146 | 147 | // Logf print an log message 148 | func (l *BaseDriver) Logf(format string, v ...any) { 149 | if l.opt.Logger != nil { 150 | l.opt.Logger.Printf(format, v...) 151 | } 152 | } 153 | 154 | // SetLastErr save last error 155 | func (l *BaseDriver) SetLastErr(err error) { 156 | if err != nil { 157 | l.lastErr = err 158 | l.Logf("redis error: %s\n", err.Error()) 159 | } 160 | } 161 | 162 | // LastErr get 163 | func (l *BaseDriver) LastErr(key string) error { 164 | return l.lastErr 165 | } 166 | 167 | // IsDebug get 168 | func (l *BaseDriver) IsDebug() bool { 169 | return l.opt.Debug 170 | } 171 | -------------------------------------------------------------------------------- /driver_file.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // FileCache definition. 14 | type FileCache struct { 15 | BaseDriver 16 | // caches in memory 17 | MemoryCache 18 | // cache directory path 19 | cacheDir string 20 | // DisableMemCache disable cache in memory 21 | DisableMemCache bool 22 | // FilePrefix cache file prefix 23 | // FilePrefix string 24 | // security key for generate cache file name. 25 | securityKey string 26 | } 27 | 28 | // NewFileCache create a FileCache instance 29 | func NewFileCache(dir string, pfxAndKey ...string) *FileCache { 30 | if dir == "" { // empty, use system tmp dir 31 | dir = os.TempDir() 32 | } 33 | 34 | c := &FileCache{ 35 | cacheDir: dir, 36 | // init a memory cache. 37 | MemoryCache: MemoryCache{caches: make(map[string]*Item)}, 38 | } 39 | 40 | if ln := len(pfxAndKey); ln > 0 { 41 | // c.prefix = pfxAndKey[0] 42 | c.opt.Prefix = pfxAndKey[0] 43 | 44 | if ln > 1 { 45 | c.securityKey = pfxAndKey[1] 46 | } 47 | } 48 | 49 | return c 50 | } 51 | 52 | // Has cache key. will check expire time 53 | func (c *FileCache) Has(key string) bool { 54 | return c.get(key) != nil 55 | } 56 | 57 | // Get value by key 58 | func (c *FileCache) Get(key string) any { 59 | return c.get(key) 60 | } 61 | 62 | func (c *FileCache) get(key string) any { 63 | // read cache from memory 64 | c.lock.RLock() 65 | val := c.MemoryCache.get(key) 66 | c.lock.RUnlock() 67 | if val != nil { 68 | return val 69 | } 70 | 71 | // read cache from file 72 | bs, err := ioutil.ReadFile(c.GetFilename(key)) 73 | if err != nil { 74 | c.SetLastErr(err) 75 | return nil 76 | } 77 | 78 | item := &Item{} 79 | if err = c.UnmarshalTo(bs, item); err != nil { 80 | c.SetLastErr(err) 81 | return nil 82 | } 83 | 84 | // check expired 85 | if item.Expired() { 86 | c.SetLastErr(c.del(key)) 87 | return nil 88 | } 89 | 90 | c.lock.Lock() 91 | c.caches[key] = item // save to memory. 92 | c.lock.Unlock() 93 | return item.Val 94 | } 95 | 96 | // Set value by key 97 | func (c *FileCache) Set(key string, val any, ttl time.Duration) (err error) { 98 | c.lock.Lock() 99 | defer c.lock.Unlock() 100 | 101 | return c.set(key, val, ttl) 102 | } 103 | 104 | func (c *FileCache) set(key string, val any, ttl time.Duration) (err error) { 105 | err = c.MemoryCache.set(key, val, ttl) 106 | if err != nil { 107 | return 108 | } 109 | 110 | // cache item data to file 111 | bs, err := c.MustMarshal(c.caches[key]) 112 | if err != nil { 113 | c.SetLastErr(err) 114 | return 115 | } 116 | 117 | file := c.GetFilename(key) 118 | dir := filepath.Dir(file) 119 | if err = os.MkdirAll(dir, 0755); err != nil { 120 | c.SetLastErr(err) 121 | return 122 | } 123 | 124 | f, err := os.Create(file) 125 | if err != nil { 126 | return err 127 | } 128 | defer f.Close() 129 | 130 | _, err = f.Write(bs) 131 | return 132 | } 133 | 134 | // Del value by key 135 | func (c *FileCache) Del(key string) error { 136 | c.lock.Lock() 137 | defer c.lock.Unlock() 138 | 139 | return c.del(key) 140 | } 141 | 142 | func (c *FileCache) del(key string) error { 143 | if err := c.MemoryCache.del(key); err != nil { 144 | return err 145 | } 146 | 147 | file := c.GetFilename(key) 148 | if fileExists(file) { 149 | return os.Remove(file) 150 | } 151 | 152 | return nil 153 | } 154 | 155 | // GetMulti values by multi key 156 | func (c *FileCache) GetMulti(keys []string) map[string]any { 157 | c.lock.RLock() 158 | defer c.lock.RUnlock() 159 | 160 | data := make(map[string]any, len(keys)) 161 | for _, key := range keys { 162 | data[key] = c.get(key) 163 | } 164 | 165 | return data 166 | } 167 | 168 | // SetMulti values by multi key 169 | func (c *FileCache) SetMulti(values map[string]any, ttl time.Duration) (err error) { 170 | c.lock.Lock() 171 | defer c.lock.Unlock() 172 | 173 | for key, val := range values { 174 | if err = c.set(key, val, ttl); err != nil { 175 | return 176 | } 177 | } 178 | return 179 | } 180 | 181 | // DelMulti values by multi key 182 | func (c *FileCache) DelMulti(keys []string) error { 183 | c.lock.Lock() 184 | defer c.lock.Unlock() 185 | 186 | for _, key := range keys { 187 | _ = c.del(key) 188 | } 189 | return nil 190 | } 191 | 192 | // Close cache 193 | func (c *FileCache) Close() error { 194 | return nil 195 | } 196 | 197 | // Clear caches and files 198 | func (c *FileCache) Clear() error { 199 | c.lock.Lock() 200 | defer c.lock.Unlock() 201 | 202 | for key := range c.caches { 203 | if file := c.GetFilename(key); fileExists(file) { 204 | err := os.Remove(file) 205 | if err != nil { 206 | return err 207 | } 208 | } 209 | } 210 | 211 | c.caches = nil 212 | // clear cache files 213 | return os.RemoveAll(c.cacheDir) 214 | } 215 | 216 | // GetFilename cache file name build 217 | func (c *FileCache) GetFilename(key string) string { 218 | h := md5.New() 219 | if c.securityKey != "" { 220 | h.Write([]byte(c.securityKey + key)) 221 | } else { 222 | h.Write([]byte(key)) 223 | } 224 | 225 | str := hex.EncodeToString(h.Sum(nil)) 226 | 227 | // return fmt.Sprintf("%s/%s/%s.data", c.cacheDir, str[0:6], c.prefix+str) 228 | return strings.Join([]string{c.cacheDir, str[0:6], c.opt.Prefix + str + ".data"}, "/") 229 | } 230 | 231 | // fileExists reports whether the named file or directory exists. 232 | func fileExists(name string) bool { 233 | if _, err := os.Stat(name); err != nil { 234 | if os.IsNotExist(err) { 235 | return false 236 | } 237 | } 238 | return true 239 | } 240 | -------------------------------------------------------------------------------- /driver_memory.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | // Item for memory cache 9 | type Item struct { 10 | // Exp expire time 11 | Exp int64 12 | // Val cache value storage 13 | Val any 14 | } 15 | 16 | // Expired check whether expired 17 | func (item Item) Expired() bool { 18 | return item.Exp > 1 && item.Exp < time.Now().Unix() 19 | } 20 | 21 | // MemoryCache definition. 22 | type MemoryCache struct { 23 | // locker 24 | lock sync.RWMutex 25 | // cache data in memory. or use sync.Map 26 | caches map[string]*Item 27 | // CacheSize TODO set max cache size 28 | CacheSize int 29 | } 30 | 31 | // NewMemoryCache create a memory cache instance 32 | func NewMemoryCache() *MemoryCache { 33 | return &MemoryCache{ 34 | caches: make(map[string]*Item), 35 | } 36 | } 37 | 38 | // Has cache key 39 | func (c *MemoryCache) Has(key string) bool { 40 | c.lock.RLock() 41 | defer c.lock.RUnlock() 42 | 43 | return c.get(key) != nil 44 | } 45 | 46 | // Get cache value by key 47 | func (c *MemoryCache) Get(key string) any { 48 | c.lock.RLock() 49 | defer c.lock.RUnlock() 50 | 51 | return c.get(key) 52 | } 53 | 54 | func (c *MemoryCache) get(key string) any { 55 | if item, ok := c.caches[key]; ok { 56 | // check expire time. if has been expired, remove it. 57 | if item.Expired() { 58 | _ = c.del(key) 59 | return nil 60 | } 61 | 62 | return item.Val 63 | } 64 | 65 | return nil 66 | } 67 | 68 | // Set cache value by key 69 | func (c *MemoryCache) Set(key string, val any, ttl time.Duration) (err error) { 70 | c.lock.Lock() 71 | defer c.lock.Unlock() 72 | 73 | return c.set(key, val, ttl) 74 | } 75 | 76 | func (c *MemoryCache) set(key string, val any, ttl time.Duration) (err error) { 77 | item := &Item{Val: val} 78 | if ttl > 0 { 79 | item.Exp = time.Now().Unix() + int64(ttl/time.Second) 80 | } 81 | 82 | c.caches[key] = item 83 | return 84 | } 85 | 86 | // Del cache by key 87 | func (c *MemoryCache) Del(key string) error { 88 | c.lock.Lock() 89 | defer c.lock.Unlock() 90 | 91 | return c.del(key) 92 | } 93 | 94 | func (c *MemoryCache) del(key string) error { 95 | if _, ok := c.caches[key]; ok { 96 | delete(c.caches, key) 97 | } 98 | 99 | return nil 100 | } 101 | 102 | // GetMulti values by multi key 103 | func (c *MemoryCache) GetMulti(keys []string) map[string]any { 104 | c.lock.RLock() 105 | 106 | data := make(map[string]any, len(keys)) 107 | for _, key := range keys { 108 | data[key] = c.get(key) 109 | } 110 | 111 | c.lock.RUnlock() 112 | return data 113 | } 114 | 115 | // SetMulti values by multi key 116 | func (c *MemoryCache) SetMulti(values map[string]any, ttl time.Duration) (err error) { 117 | c.lock.Lock() 118 | for key, val := range values { 119 | if err = c.set(key, val, ttl); err != nil { 120 | return 121 | } 122 | } 123 | 124 | c.lock.Unlock() 125 | return 126 | } 127 | 128 | // DelMulti values by multi key 129 | func (c *MemoryCache) DelMulti(keys []string) error { 130 | c.lock.Lock() 131 | for _, key := range keys { 132 | _ = c.del(key) 133 | } 134 | 135 | c.lock.Unlock() 136 | return nil 137 | } 138 | 139 | // Close cache 140 | func (c *MemoryCache) Close() error { 141 | return nil 142 | } 143 | 144 | // Clear all caches 145 | func (c *MemoryCache) Clear() error { 146 | c.caches = nil 147 | return nil 148 | } 149 | 150 | // Count cache item number 151 | func (c *MemoryCache) Count() int { 152 | return len(c.caches) 153 | } 154 | 155 | // Restore DB from a file 156 | func (c *MemoryCache) Restore(file string) error { 157 | return nil 158 | } 159 | 160 | // DumpDB to a file 161 | func (c *MemoryCache) DumpDB(file string) error { 162 | return nil 163 | } 164 | 165 | // Iter iteration all caches 166 | func (c *MemoryCache) Iter(file string) error { 167 | return nil 168 | } 169 | -------------------------------------------------------------------------------- /driver_test.go: -------------------------------------------------------------------------------- 1 | package cache_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/gookit/cache" 8 | "github.com/gookit/goutil/dump" 9 | "github.com/gookit/goutil/strutil" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestNewMemoryCache(t *testing.T) { 14 | is := assert.New(t) 15 | c := cache.NewMemoryCache() 16 | 17 | key := "key" 18 | is.False(c.Has(key)) 19 | 20 | err := c.Set(key, "value", cache.Seconds3) 21 | is.NoError(err) 22 | is.True(c.Has(key)) 23 | 24 | val := c.Get(key) 25 | is.Equal("value", val) 26 | 27 | // del 28 | err = c.Del(key) 29 | is.NoError(err) 30 | is.False(c.Has(key)) 31 | } 32 | 33 | type user struct { 34 | Age int 35 | Name string 36 | } 37 | 38 | func TestMemoryCache_object(t *testing.T) { 39 | is := assert.New(t) 40 | b1 := user { 41 | Age: 1, 42 | Name: "inhere", 43 | } 44 | 45 | c := cache.NewMemoryCache() 46 | 47 | key := strutil.RandomCharsV2(12) 48 | dump.P("cache key: " + key) 49 | 50 | is.False(c.Has(key)) 51 | 52 | err := c.Set(key, b1, cache.Seconds3) 53 | is.NoError(err) 54 | is.True(c.Has(key)) 55 | 56 | b2 := c.Get(key).(user) 57 | dump.P(b2) 58 | is.Equal("inhere", b2.Name) 59 | } 60 | 61 | func TestMemoryCache_expired(t *testing.T) { 62 | is := assert.New(t) 63 | c := cache.NewMemoryCache() 64 | 65 | key := "key" 66 | is.False(c.Has(key)) 67 | 68 | err := c.Set(key, "value", cache.Seconds1) 69 | is.NoError(err) 70 | is.Equal("value", c.Get(key)) 71 | 72 | time.Sleep(cache.Seconds2) 73 | 74 | is.Nil(c.Get(key)) 75 | } 76 | 77 | func TestNewFileCache(t *testing.T) { 78 | is := assert.New(t) 79 | c := cache.NewFileCache("./testdata") 80 | 81 | key := "key" 82 | is.False(c.Has(key)) 83 | 84 | // set 85 | err := c.Set(key, "cache value", cache.OneMinutes) 86 | is.NoError(err) 87 | is.True(c.Has(key)) 88 | 89 | err = c.Set("key2", "cache value2", cache.TwoMinutes) 90 | is.NoError(err) 91 | 92 | // get 93 | val := c.Get(key) 94 | is.Equal("cache value", val) 95 | 96 | // del 97 | err = c.Del(key) 98 | is.NoError(err) 99 | is.False(c.Has(key)) 100 | } 101 | 102 | func TestFileCache_object(t *testing.T) { 103 | is := assert.New(t) 104 | c := cache.NewFileCache("./testdata") 105 | c.WithOptions(cache.WithEncode(true)) 106 | 107 | b1 := user { 108 | Age: 12, 109 | Name: "inhere", 110 | } 111 | 112 | key := strutil.RandomCharsV2(12) 113 | dump.P("cache key: " + c.Key(key)) 114 | 115 | err := c.Set(key, b1, cache.Seconds3) 116 | is.NoError(err) 117 | is.True(c.Has(key)) 118 | 119 | val := c.Get(key) 120 | dump.P("cache get:", val) 121 | 122 | // val2 := c.GetAs() 123 | // dump.P("cache get:", val) 124 | } 125 | 126 | func TestDefManager(t *testing.T) { 127 | is := assert.New(t) 128 | num := cache.UnregisterAll() 129 | is.Equal(0, num) 130 | is.Equal(0, cache.Unregister("not_exist")) 131 | 132 | cache.Register(cache.DvrMemory, cache.NewMemoryCache()) 133 | is.Equal(cache.DvrMemory, cache.Std().DefName()) 134 | 135 | key := "name" 136 | 137 | // set 138 | err := cache.Set(key, "cache value", cache.TwoMinutes) 139 | is.NoError(err) 140 | is.True(cache.Has(key)) 141 | 142 | // get 143 | val := cache.Get(key) 144 | is.Equal("cache value", val) 145 | 146 | // del 147 | is.NoError(cache.Del(key)) 148 | is.False(cache.Has(key)) 149 | 150 | is.NoError(cache.Clear()) 151 | 152 | num = cache.UnregisterAll() 153 | is.GreaterOrEqual(1, num) 154 | } 155 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package cache_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gookit/cache" 7 | "github.com/gookit/cache/goredis" 8 | "github.com/gookit/cache/redis" 9 | "github.com/gookit/goutil/dump" 10 | ) 11 | 12 | func Example() { 13 | // register some cache driver 14 | cache.Register(cache.DvrFile, cache.NewFileCache("")) 15 | cache.Register(cache.DvrMemory, cache.NewMemoryCache()) 16 | cache.Register(redis.Name, redis.Connect("127.0.0.1:6379", "", 0)) 17 | cache.Register(goredis.Name, goredis.Connect("127.0.0.1:6379", "", 0)) 18 | 19 | // setting default driver name 20 | cache.DefaultUse(goredis.Name) 21 | 22 | // quick use.(it is default driver) 23 | // 24 | // set 25 | _ = cache.Set("name", "cache value", cache.TwoMinutes) 26 | // get 27 | val := cache.Get("name") 28 | // del 29 | _ = cache.Del("name") 30 | 31 | // get: "cache value" 32 | fmt.Print(val) 33 | 34 | // More ... 35 | // fc := cache.GetCache(DvrFile) 36 | // fc.Set("key", "value", 10) 37 | // fc.Get("key") 38 | } 39 | 40 | func ExampleMemoryCache() { 41 | c := cache.NewMemoryCache() 42 | key := "name" 43 | 44 | // set 45 | c.Set(key, "cache value", cache.TwoMinutes) 46 | fmt.Println(c.Has(key), c.Count()) 47 | 48 | // get 49 | val := c.Get(key) 50 | fmt.Println(val) 51 | 52 | // del 53 | c.Del(key) 54 | fmt.Println(c.Has(key), c.Count()) 55 | 56 | // Output: 57 | // true 1 58 | // cache value 59 | // false 0 60 | } 61 | 62 | func ExampleFileCache() { 63 | c := cache.NewFileCache("./testdata") 64 | key := "name" 65 | 66 | // set 67 | c.Set(key, "cache value", cache.TwoMinutes) 68 | fmt.Println(c.Has(key)) 69 | 70 | // get 71 | val := c.Get(key) 72 | fmt.Println(val) 73 | 74 | // del 75 | c.Del(key) 76 | fmt.Println(c.Has(key)) 77 | 78 | // Output: 79 | // true 80 | // cache value 81 | // false 82 | } 83 | 84 | func Example_withOptions() { 85 | gords := goredis.Connect("127.0.0.1:6379", "", 0) 86 | gords.WithOptions(cache.WithPrefix("cache_"), cache.WithEncode(true)) 87 | 88 | // register 89 | cache.Register(goredis.Name, gords) 90 | 91 | // set 92 | // real key is: "cache_name" 93 | cache.Set("name", "cache value", cache.TwoMinutes) 94 | 95 | // get: "cache value" 96 | val := cache.Get("name") 97 | 98 | dump.P(val) 99 | } -------------------------------------------------------------------------------- /gcache/gcache.go: -------------------------------------------------------------------------------- 1 | // Package gcache use the github.com/bluele/gcache as cache driver 2 | package gcache 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/bluele/gcache" 8 | ) 9 | 10 | // Name driver name 11 | const Name = "gcache" 12 | 13 | // GCache driver definition 14 | type GCache struct { 15 | // cache.BaseDriver 16 | db gcache.Cache 17 | } 18 | 19 | // New create an instance 20 | func New(size int) *GCache { 21 | return NewWithType(size, gcache.TYPE_LRU) 22 | } 23 | 24 | // NewWithType create an instance with cache type 25 | func NewWithType(size int, tp string) *GCache { 26 | return &GCache{ 27 | db: gcache.New(size).EvictType(tp).Build(), 28 | } 29 | } 30 | 31 | // Close connection 32 | func (g *GCache) Close() error { 33 | return nil 34 | } 35 | 36 | // Clear all caches 37 | func (g *GCache) Clear() error { 38 | g.db.Purge() 39 | return nil 40 | } 41 | 42 | // Has cache key 43 | func (g *GCache) Has(key string) bool { 44 | return g.Get(key) != nil 45 | } 46 | 47 | // Get cache by key 48 | func (g *GCache) Get(key string) any { 49 | val, _ := g.db.Get(key) 50 | return val 51 | } 52 | 53 | // Set cache by key 54 | func (g *GCache) Set(key string, val any, ttl time.Duration) (err error) { 55 | return g.db.SetWithExpire(key, val, ttl) 56 | } 57 | 58 | // Del cache by key 59 | func (g *GCache) Del(key string) error { 60 | g.db.Remove(key) 61 | return nil 62 | } 63 | 64 | // GetMulti cache by keys 65 | func (g *GCache) GetMulti(keys []string) map[string]any { 66 | data := make(map[string]any, len(keys)) 67 | 68 | for _, key := range keys { 69 | val, err := g.db.Get(key) 70 | if err == nil { 71 | data[key] = val 72 | } // TODO log error 73 | } 74 | 75 | return data 76 | } 77 | 78 | // SetMulti cache by keys 79 | func (g *GCache) SetMulti(values map[string]any, ttl time.Duration) (err error) { 80 | for key, val := range values { 81 | err = g.db.SetWithExpire(key, val, ttl) 82 | } 83 | return 84 | } 85 | 86 | // DelMulti cache by keys 87 | func (g *GCache) DelMulti(keys []string) error { 88 | for _, key := range keys { 89 | g.db.Remove(key) 90 | } 91 | return nil 92 | } 93 | 94 | // Db get the gcache.Cache 95 | func (g *GCache) Db() gcache.Cache { 96 | return g.db 97 | } 98 | -------------------------------------------------------------------------------- /gcache/gcache_test.go: -------------------------------------------------------------------------------- 1 | package gcache_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/gookit/cache" 9 | "github.com/gookit/cache/gcache" 10 | "github.com/gookit/goutil/dump" 11 | "github.com/gookit/goutil/strutil" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func Example() { 16 | c := gcache.New(12) 17 | key := "name" 18 | 19 | // set 20 | c.Set(key, "cache value", cache.Seconds2) 21 | fmt.Println(c.Has(key)) 22 | 23 | // get 24 | val := c.Get(key) 25 | fmt.Println(val) 26 | 27 | time.Sleep(2 * time.Second) 28 | 29 | // get expired 30 | val2 := c.Get(key) 31 | fmt.Println(val2) 32 | 33 | // del 34 | c.Del(key) 35 | fmt.Println(c.Has(key)) 36 | 37 | // Output: 38 | // true 39 | // cache value 40 | // 41 | // false 42 | } 43 | 44 | func ExampleGCache_in_cachePkg() { 45 | cache.Register(gcache.Name, gcache.New(12)) 46 | defer cache.UnregisterAll() 47 | 48 | key := "name1" 49 | 50 | // set 51 | cache.Set(key, "cache value", cache.Seconds2) 52 | fmt.Println(cache.Has(key)) 53 | 54 | // get 55 | val := cache.Get(key) 56 | fmt.Println(val) 57 | 58 | time.Sleep(2 * time.Second) 59 | 60 | // get expired 61 | val2 := cache.Get(key) 62 | fmt.Println(val2) 63 | 64 | // del 65 | cache.Del(key) 66 | fmt.Println(cache.Has(key)) 67 | 68 | // Output: 69 | // true 70 | // cache value 71 | // 72 | // false 73 | } 74 | 75 | func TestGCache_usage(t *testing.T) { 76 | is := assert.New(t) 77 | c := gcache.New(12) 78 | defer c.Clear() 79 | 80 | key := strutil.RandomCharsV2(12) 81 | is.False(c.Has(key)) 82 | 83 | err := c.Set(key, "value", cache.Seconds3) 84 | is.NoError(err) 85 | is.True(c.Has(key)) 86 | 87 | val := c.Get(key) 88 | is.Equal("value", val) 89 | 90 | // del 91 | err = c.Del(key) 92 | is.NoError(err) 93 | is.False(c.Has(key)) 94 | } 95 | 96 | type user struct { 97 | Age int 98 | Name string 99 | } 100 | 101 | func TestGCache_object(t *testing.T) { 102 | is := assert.New(t) 103 | c := gcache.New(12) 104 | defer c.Clear() 105 | 106 | b1 := user { 107 | Age: 1, 108 | Name: "inhere", 109 | } 110 | 111 | key := strutil.RandomCharsV2(12) 112 | dump.P("cache key: " + key) 113 | is.False(c.Has(key)) 114 | 115 | err := c.Set(key, b1, cache.Seconds3) 116 | is.NoError(err) 117 | is.True(c.Has(key)) 118 | 119 | b2 := c.Get(key).(user) 120 | dump.P(b2) 121 | is.Equal("inhere", b2.Name) 122 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gookit/cache 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | github.com/bluele/gcache v0.0.2 9 | github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746 10 | github.com/dgraph-io/badger v1.6.2 11 | github.com/gomodule/redigo v1.9.2 12 | github.com/gookit/goutil v0.6.18 13 | github.com/gookit/gsr v0.1.1 14 | github.com/patrickmn/go-cache v2.1.0+incompatible 15 | github.com/redis/go-redis/v9 v9.9.0 16 | github.com/stretchr/testify v1.10.0 17 | github.com/syndtr/goleveldb v1.0.0 18 | github.com/tidwall/buntdb v1.3.2 19 | go.etcd.io/bbolt v1.4.0 20 | ) 21 | 22 | require ( 23 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect 24 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 25 | github.com/davecgh/go-spew v1.1.1 // indirect 26 | github.com/dgraph-io/ristretto v0.1.1 // indirect 27 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect 28 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 29 | github.com/dustin/go-humanize v1.0.1 // indirect 30 | github.com/golang/glog v1.2.4 // indirect 31 | github.com/golang/protobuf v1.5.2 // indirect 32 | github.com/golang/snappy v0.0.4 // indirect 33 | github.com/gookit/color v1.5.4 // indirect 34 | github.com/pkg/errors v0.9.1 // indirect 35 | github.com/pmezard/go-difflib v1.0.0 // indirect 36 | github.com/tidwall/btree v1.6.0 // indirect 37 | github.com/tidwall/gjson v1.14.4 // indirect 38 | github.com/tidwall/grect v0.1.4 // indirect 39 | github.com/tidwall/match v1.1.1 // indirect 40 | github.com/tidwall/pretty v1.2.1 // indirect 41 | github.com/tidwall/rtred v0.1.2 // indirect 42 | github.com/tidwall/tinyqueue v0.1.1 // indirect 43 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 44 | golang.org/x/net v0.38.0 // indirect 45 | golang.org/x/sys v0.31.0 // indirect 46 | golang.org/x/text v0.23.0 // indirect 47 | google.golang.org/protobuf v1.33.0 // indirect 48 | gopkg.in/yaml.v3 v3.0.1 // indirect 49 | ) 50 | 51 | exclude github.com/gomodule/redigo v2.0.0+incompatible 52 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= 2 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 5 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 6 | github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= 7 | github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= 8 | github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746 h1:wAIE/kN63Oig1DdOzN7O+k4AbFh2cCJoKMFXrwRJtzk= 9 | github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= 10 | github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= 11 | github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 12 | github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= 13 | github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 14 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 15 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 16 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 17 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 18 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 19 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 20 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 21 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 22 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 24 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= 26 | github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= 27 | github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= 28 | github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= 29 | github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= 30 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 31 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= 32 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 33 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 34 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 35 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 36 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 37 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 38 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 39 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 40 | github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= 41 | github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= 42 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 43 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 44 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 45 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 46 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 47 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 48 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 49 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 50 | github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s= 51 | github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= 52 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 53 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 54 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 55 | github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= 56 | github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= 57 | github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw= 58 | github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA= 59 | github.com/gookit/gsr v0.1.1 h1:TaHD3M7qa6lcAf9D2J4mGNg+QjgDtD1bw7uctF8RXOM= 60 | github.com/gookit/gsr v0.1.1/go.mod h1:7wv4Y4WCnil8+DlDYHBjidzrEzfHhXEoFjEA0pPPWpI= 61 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 62 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 63 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 64 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 65 | github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 66 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 67 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 68 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 69 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 70 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 71 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 72 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 73 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 74 | github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= 75 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 76 | github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= 77 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 78 | github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= 79 | github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 80 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 81 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 82 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 83 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 84 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 85 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 86 | github.com/redis/go-redis/v9 v9.9.0 h1:URbPQ4xVQSQhZ27WMQVmZSo3uT3pL+4IdHVcYq2nVfM= 87 | github.com/redis/go-redis/v9 v9.9.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= 88 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 89 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 90 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 91 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 92 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 93 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 94 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 95 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 96 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 97 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 98 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 99 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 100 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 101 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 102 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= 103 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 104 | github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= 105 | github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= 106 | github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= 107 | github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= 108 | github.com/tidwall/buntdb v1.3.2 h1:qd+IpdEGs0pZci37G4jF51+fSKlkuUTMXuHhXL1AkKg= 109 | github.com/tidwall/buntdb v1.3.2/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= 110 | github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 111 | github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= 112 | github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 113 | github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= 114 | github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= 115 | github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= 116 | github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= 117 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 118 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 119 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 120 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 121 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 122 | github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= 123 | github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= 124 | github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= 125 | github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= 126 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 127 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 128 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 129 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 130 | go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= 131 | go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= 132 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 133 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 134 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= 135 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= 136 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 137 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 138 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 139 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 140 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 141 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= 142 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 143 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 144 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 145 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 146 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 148 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 149 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 150 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 151 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 152 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 153 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 154 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 155 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 156 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 157 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 158 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 159 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 160 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 161 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 162 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 163 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 164 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 165 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 166 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 167 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 168 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 169 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 170 | -------------------------------------------------------------------------------- /gocache/gocache.go: -------------------------------------------------------------------------------- 1 | // Package gocache is a memory cache driver implement. 2 | // base on the package: github.com/patrickmn/go-cache 3 | // 4 | // Usage: 5 | // 6 | // import "github.com/gookit/cache" 7 | // 8 | // cache.Register(gocache.NewGoCache(0, cache.FiveMinutes)) 9 | // // use 10 | // // cache.Set("key", "value") 11 | package gocache 12 | 13 | import ( 14 | "time" 15 | 16 | goc "github.com/patrickmn/go-cache" 17 | ) 18 | 19 | // Name driver name 20 | const Name = "gocache" 21 | 22 | // GoCache struct 23 | type GoCache struct { 24 | db *goc.Cache 25 | // will handle expire on has,get 26 | expireManually bool 27 | } 28 | 29 | // New create instance 30 | func New() *GoCache { 31 | return NewSimple() 32 | } 33 | 34 | // NewSimple create new simple instance 35 | func NewSimple() *GoCache { 36 | return &GoCache{ 37 | db: goc.New(goc.NoExpiration, goc.NoExpiration), 38 | // handle expire on has,get 39 | expireManually: true, 40 | } 41 | } 42 | 43 | // NewGoCache create instance with settings 44 | func NewGoCache(defaultExpiration, cleanupInterval time.Duration) *GoCache { 45 | return &GoCache{ 46 | db: goc.New(defaultExpiration, cleanupInterval), 47 | } 48 | } 49 | 50 | // Close connection 51 | func (g *GoCache) Close() error { 52 | return nil 53 | } 54 | 55 | // Clear all caches 56 | func (g *GoCache) Clear() error { 57 | g.db.Flush() 58 | return nil 59 | } 60 | 61 | // Has cache key 62 | func (g *GoCache) Has(key string) bool { 63 | return g.Get(key) != nil 64 | } 65 | 66 | // Get cache by key 67 | func (g *GoCache) Get(key string) any { 68 | if g.expireManually { 69 | g.db.DeleteExpired() 70 | } 71 | 72 | val, _ := g.db.Get(key) 73 | return val 74 | } 75 | 76 | // Set cache by key 77 | func (g *GoCache) Set(key string, val any, ttl time.Duration) error { 78 | g.db.Set(key, val, ttl) 79 | return nil 80 | } 81 | 82 | // Del cache by key 83 | func (g GoCache) Del(key string) error { 84 | g.db.Delete(key) 85 | return nil 86 | } 87 | 88 | // GetMulti cache by keys 89 | func (g *GoCache) GetMulti(keys []string) map[string]any { 90 | data := make(map[string]any, len(keys)) 91 | 92 | for _, key := range keys { 93 | val, ok := g.db.Get(key) 94 | if ok { 95 | data[key] = val 96 | } 97 | } 98 | 99 | return data 100 | } 101 | 102 | // SetMulti cache by keys 103 | func (g GoCache) SetMulti(values map[string]any, ttl time.Duration) error { 104 | for key, val := range values { 105 | g.db.Set(key, val, ttl) 106 | } 107 | return nil 108 | } 109 | 110 | // DelMulti db by keys 111 | func (g *GoCache) DelMulti(keys []string) error { 112 | for _, key := range keys { 113 | g.db.Delete(key) 114 | } 115 | return nil 116 | } 117 | 118 | // Db get the goc.Cache 119 | func (g *GoCache) Db() *goc.Cache { 120 | return g.db 121 | } 122 | -------------------------------------------------------------------------------- /gocache/gocache_test.go: -------------------------------------------------------------------------------- 1 | package gocache_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/gookit/cache" 9 | "github.com/gookit/cache/gocache" 10 | "github.com/gookit/goutil/dump" 11 | "github.com/gookit/goutil/strutil" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func Example() { 16 | c := gocache.New() 17 | key := "name" 18 | 19 | // set 20 | c.Set(key, "cache value", cache.Seconds2) 21 | fmt.Println(c.Has(key)) 22 | 23 | // get 24 | val := c.Get(key) 25 | fmt.Println(val) 26 | 27 | time.Sleep(2 * time.Second) 28 | 29 | // get expired 30 | val2 := c.Get(key) 31 | fmt.Println(val2) 32 | 33 | // del 34 | c.Del(key) 35 | fmt.Println(c.Has(key)) 36 | 37 | // Output: 38 | // true 39 | // cache value 40 | // 41 | // false 42 | } 43 | 44 | func ExampleGoCache_in_cachePkg() { 45 | c1 := gocache.NewGoCache(cache.OneDay, cache.FiveMinutes) 46 | cache.Register(gocache.Name, c1) 47 | defer cache.UnregisterAll() 48 | 49 | key := "name1" 50 | 51 | // set 52 | cache.Set(key, "cache value", cache.Seconds2) 53 | fmt.Println(cache.Has(key)) 54 | 55 | // get 56 | val := cache.Get(key) 57 | fmt.Println(val) 58 | 59 | time.Sleep(2 * time.Second) 60 | 61 | // get expired 62 | val2 := cache.Get(key) 63 | fmt.Println(val2) 64 | 65 | // del 66 | cache.Del(key) 67 | fmt.Println(cache.Has(key)) 68 | 69 | // Output: 70 | // true 71 | // cache value 72 | // 73 | // false 74 | } 75 | 76 | func TestGoCache_usage(t *testing.T) { 77 | is := assert.New(t) 78 | c := gocache.NewSimple() 79 | defer c.Clear() 80 | 81 | key := strutil.RandomCharsV2(12) 82 | is.False(c.Has(key)) 83 | 84 | err := c.Set(key, "value", cache.Seconds3) 85 | is.NoError(err) 86 | is.True(c.Has(key)) 87 | 88 | val := c.Get(key) 89 | is.Equal("value", val) 90 | 91 | // del 92 | err = c.Del(key) 93 | is.NoError(err) 94 | is.False(c.Has(key)) 95 | } 96 | 97 | type user struct { 98 | Age int 99 | Name string 100 | } 101 | 102 | func TestGoCache_object(t *testing.T) { 103 | is := assert.New(t) 104 | c := gocache.NewSimple() 105 | defer c.Clear() 106 | 107 | b1 := user { 108 | Age: 1, 109 | Name: "inhere", 110 | } 111 | 112 | key := strutil.RandomCharsV2(12) 113 | dump.P("cache key: " + key) 114 | is.False(c.Has(key)) 115 | 116 | err := c.Set(key, b1, cache.Seconds3) 117 | is.NoError(err) 118 | is.True(c.Has(key)) 119 | 120 | b2 := c.Get(key).(user) 121 | dump.P(b2) 122 | is.Equal("inhere", b2.Name) 123 | } -------------------------------------------------------------------------------- /goredis/goredis.go: -------------------------------------------------------------------------------- 1 | // Package goredis is a simple redis cache implement. 2 | // base on the package: github.com/go-redis/redis 3 | package goredis 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "time" 9 | 10 | "github.com/gookit/cache" 11 | "github.com/gookit/gsr" 12 | "github.com/redis/go-redis/v9" 13 | ) 14 | 15 | // Name driver name 16 | const Name = "goredis" 17 | 18 | // CtxForExec default ctx for exec command 19 | var CtxForExec = context.Background() 20 | 21 | // GoRedis struct 22 | type GoRedis struct { 23 | cache.BaseDriver 24 | // client 25 | rdb *redis.Client 26 | ctx context.Context 27 | // config 28 | url string 29 | pwd string 30 | dbNum int 31 | } 32 | 33 | // Connect create and connect to redis server 34 | func Connect(url, pwd string, dbNum int) *GoRedis { 35 | return New(url, pwd, dbNum).Connect() 36 | } 37 | 38 | // New redis cache 39 | func New(url, pwd string, dbNum int) *GoRedis { 40 | rc := &GoRedis{ 41 | url: url, pwd: pwd, dbNum: dbNum, 42 | ctx: CtxForExec, 43 | } 44 | 45 | return rc 46 | } 47 | 48 | // String get 49 | func (c *GoRedis) String() string { 50 | pwd := "*" 51 | if c.IsDebug() { 52 | pwd = c.pwd 53 | } 54 | 55 | return fmt.Sprintf("connection info. url: %s, pwd: %s, dbNum: %d", c.url, pwd, c.dbNum) 56 | } 57 | 58 | // Connect to redis server 59 | func (c *GoRedis) Connect() *GoRedis { 60 | c.rdb = redis.NewClient(&redis.Options{ 61 | Addr: c.url, 62 | Password: c.pwd, // no password set 63 | DB: c.dbNum, // use default DB 64 | }) 65 | c.Logf("connect to server %s db is %d", c.url, c.dbNum) 66 | 67 | return c 68 | } 69 | 70 | /************************************************************* 71 | * methods implements of the gsr.SimpleCacher 72 | *************************************************************/ 73 | 74 | // WithContext for operate 75 | func (c *GoRedis) WithContext(ctx context.Context) gsr.ContextCacher { 76 | cp := *c 77 | cp.ctx = ctx 78 | return &cp 79 | } 80 | 81 | // Close connection 82 | func (c *GoRedis) Close() error { 83 | return c.rdb.Close() 84 | } 85 | 86 | // Clear all caches 87 | func (c *GoRedis) Clear() error { 88 | return c.rdb.FlushDB(c.ctx).Err() 89 | } 90 | 91 | // Has cache key 92 | func (c *GoRedis) Has(key string) bool { 93 | n, err := c.rdb.Exists(c.ctx, c.Key(key)).Result() 94 | if err != nil { 95 | c.SetLastErr(err) 96 | return false 97 | } 98 | 99 | return n == 1 100 | } 101 | 102 | // Get cache by key 103 | func (c *GoRedis) Get(key string) any { 104 | bts, err := c.rdb.Get(c.ctx, c.Key(key)).Bytes() 105 | 106 | return c.Unmarshal(bts, err) 107 | } 108 | 109 | // GetAs get cache and unmarshal to ptr 110 | func (c *GoRedis) GetAs(key string, ptr any) error { 111 | bts, err := c.rdb.Get(c.ctx, c.Key(key)).Bytes() 112 | if err != nil { 113 | return err 114 | } 115 | 116 | return c.UnmarshalTo(bts, ptr) 117 | } 118 | 119 | // Set cache by key 120 | func (c *GoRedis) Set(key string, val any, ttl time.Duration) (err error) { 121 | val, err = c.Marshal(val) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | return c.rdb.SetNX(c.ctx, c.Key(key), val, ttl).Err() 127 | } 128 | 129 | // Del caches by key 130 | func (c *GoRedis) Del(key string) error { 131 | return c.rdb.Del(c.ctx, c.Key(key)).Err() 132 | } 133 | 134 | // GetMulti cache by keys 135 | func (c *GoRedis) GetMulti(keys []string) map[string]any { 136 | panic("implement me") 137 | } 138 | 139 | // SetMulti cache by keys 140 | func (c *GoRedis) SetMulti(values map[string]any, ttl time.Duration) (err error) { 141 | panic("implement me") 142 | } 143 | 144 | // DelMulti cache by keys 145 | func (c *GoRedis) DelMulti(keys []string) error { 146 | cks := make([]string, 0, len(keys)) 147 | for _, key := range keys { 148 | cks = append(cks, c.Key(key)) 149 | } 150 | 151 | return c.rdb.Del(c.ctx, cks...).Err() 152 | } 153 | -------------------------------------------------------------------------------- /goredis/goredis_test.go: -------------------------------------------------------------------------------- 1 | package goredis_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/gookit/cache" 8 | "github.com/gookit/cache/goredis" 9 | "github.com/gookit/goutil/dump" 10 | "github.com/gookit/goutil/strutil" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Example() { 15 | // init driver 16 | c := goredis.Connect("127.0.0.1:6379", "", 0) 17 | 18 | // set 19 | _ = c.Set("name", "cache value", 60) 20 | 21 | // get 22 | val := c.Get("name") 23 | 24 | // del 25 | _ = c.Del("name") 26 | 27 | // get: "cache value" 28 | fmt.Print(val) 29 | } 30 | 31 | var c *goredis.GoRedis 32 | 33 | func getC() *goredis.GoRedis { 34 | if c != nil { 35 | return c 36 | } 37 | 38 | c = goredis.New("127.0.0.1:6379", "", 0).Connect() 39 | c.WithOptions(cache.WithPrefix("gr"), cache.WithEncode(true)) 40 | 41 | return c 42 | } 43 | 44 | func TestGoRedis_basic(t *testing.T) { 45 | c := getC() 46 | 47 | key := strutil.RandomCharsV2(12) 48 | dump.P("cache key: " + c.Key(key)) 49 | 50 | assert.False(t, c.Has(key)) 51 | 52 | err := c.Set(key, "value", cache.Forever) 53 | assert.NoError(t, err) 54 | 55 | assert.True(t, c.Has(key)) 56 | assert.Equal(t, "value", c.Get(key).(string)) 57 | 58 | err = c.Del(key) 59 | assert.NoError(t, err) 60 | 61 | assert.False(t, c.Has(key)) 62 | } 63 | 64 | type user struct { 65 | Age int 66 | Name string 67 | } 68 | 69 | func TestRedigo_object(t *testing.T) { 70 | c := getC() 71 | u1 := user { 72 | Age: 12, 73 | Name: "inhere", 74 | } 75 | 76 | key := strutil.RandomCharsV2(12) 77 | dump.P("cache key: " + c.Key(key)) 78 | 79 | assert.False(t, c.Has(key)) 80 | 81 | err := c.Set(key, u1, cache.Seconds3) 82 | assert.NoError(t, err) 83 | assert.True(t, c.Has(key)) 84 | 85 | v := c.Get(key) 86 | assert.NotEmpty(t, v) 87 | 88 | dump.P(v) 89 | 90 | u2 := user{} 91 | err = c.GetAs(key, &u2) 92 | assert.NoError(t, err) 93 | dump.P(u2) 94 | assert.Equal(t, "inhere", u2.Name) 95 | 96 | err = c.Del(key) 97 | assert.NoError(t, err) 98 | assert.False(t, c.Has(key)) 99 | assert.Empty(t, c.Get(key)) 100 | } 101 | -------------------------------------------------------------------------------- /helper.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | ) 7 | 8 | // BindStruct get cache value and map to a struct 9 | func BindStruct(val any, ptr any) error { 10 | // val must convert to byte 11 | return Unmarshal(val.([]byte), ptr) 12 | } 13 | 14 | // GobDecode decode data by gob.Decode 15 | func GobDecode(bts []byte, ptr any) error { 16 | buf := bytes.NewBuffer(bts) 17 | dec := gob.NewDecoder(buf) 18 | 19 | return dec.Decode(ptr) 20 | } 21 | 22 | // GobEncode encode data by gob.Encode 23 | func GobEncode(val any) (bs []byte, err error) { 24 | buf := new(bytes.Buffer) 25 | enc := gob.NewEncoder(buf) 26 | if err = enc.Encode(val); err != nil { 27 | return 28 | } 29 | 30 | return buf.Bytes(), nil 31 | } 32 | -------------------------------------------------------------------------------- /leveldb/leveldb.go: -------------------------------------------------------------------------------- 1 | // Package leveldb use the https://github.com/syndtr/goleveldb as cache driver 2 | package leveldb 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/syndtr/goleveldb/leveldb" 8 | ) 9 | 10 | // Name driver name 11 | const Name = "leveldb" 12 | 13 | // LevelDB definition 14 | type LevelDB struct { 15 | // cache.BaseDriver 16 | db *leveldb.DB 17 | } 18 | 19 | func (c *LevelDB) Has(key string) bool { 20 | panic("implement me") 21 | } 22 | 23 | func (c *LevelDB) Get(key string) any { 24 | panic("implement me") 25 | } 26 | 27 | func (c *LevelDB) Set(key string, val any, ttl time.Duration) (err error) { 28 | panic("implement me") 29 | } 30 | 31 | func (c *LevelDB) Del(key string) error { 32 | panic("implement me") 33 | } 34 | 35 | func (c *LevelDB) GetMulti(keys []string) map[string]any { 36 | panic("implement me") 37 | } 38 | 39 | func (c *LevelDB) SetMulti(values map[string]any, ttl time.Duration) (err error) { 40 | panic("implement me") 41 | } 42 | 43 | func (c *LevelDB) DelMulti(keys []string) error { 44 | panic("implement me") 45 | } 46 | 47 | func (c *LevelDB) Clear() error { 48 | panic("implement me") 49 | } 50 | 51 | func (c *LevelDB) Close() error { 52 | return c.db.Close() 53 | } 54 | -------------------------------------------------------------------------------- /manager.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "time" 4 | 5 | // default supported cache driver name 6 | const ( 7 | DvrFile = "file" 8 | DvrRedis = "redis" 9 | DvrMemory = "memory" 10 | DvrMemCached = "memCached" 11 | DvrBoltDB = "boltDB" 12 | DvrBuntDB = "buntDB" 13 | ) 14 | 15 | /************************************************************* 16 | * Cache Manager 17 | *************************************************************/ 18 | 19 | // Manager definition 20 | type Manager struct { 21 | // Debug bool 22 | // default driver name 23 | defName string 24 | // drivers map 25 | drivers map[string]Cache 26 | } 27 | 28 | // NewManager create a cache manager instance 29 | func NewManager() *Manager { 30 | return &Manager{ 31 | // defName: driverName, 32 | drivers: make(map[string]Cache, 8), 33 | } 34 | } 35 | 36 | // Register new cache driver 37 | func (m *Manager) Register(name string, driver Cache) *Manager { 38 | // always use latest as default driver. 39 | m.defName = name 40 | // save driver instance 41 | m.drivers[name] = driver 42 | return m 43 | } 44 | 45 | // Unregister an cache driver 46 | func (m *Manager) Unregister(name string) int { 47 | if _, ok := m.drivers[name]; !ok { 48 | return 0 49 | } 50 | 51 | delete(m.drivers, name) 52 | 53 | // reset default driver name. 54 | if m.defName == name { 55 | m.defName = "" 56 | } 57 | return 1 58 | } 59 | 60 | // SetDefName set default driver name. alias of DefaultUse() 61 | // Deprecated 62 | // 63 | // please use DefaultUse() instead it 64 | func (m *Manager) SetDefName(driverName string) { 65 | m.DefaultUse(driverName) 66 | } 67 | 68 | // DefaultUse set default driver name 69 | func (m *Manager) DefaultUse(driverName string) { 70 | if _, ok := m.drivers[driverName]; !ok { 71 | panic("cache driver: " + driverName + " is not registered") 72 | } 73 | 74 | m.defName = driverName 75 | } 76 | 77 | // Default returns the default driver instance 78 | func (m *Manager) Default() Cache { 79 | if c, ok := m.drivers[m.defName]; ok { 80 | return c 81 | } 82 | 83 | panic("cache driver: " + m.defName + " is not registered") 84 | } 85 | 86 | // Use driver object by name and set it as default driver. 87 | func (m *Manager) Use(driverName string) Cache { 88 | m.DefaultUse(driverName) 89 | return m.Driver(driverName) 90 | } 91 | 92 | // Cache get driver by name. alias of Driver() 93 | func (m *Manager) Cache(driverName string) Cache { 94 | return m.drivers[driverName] 95 | } 96 | 97 | // Driver get a driver instance by name 98 | func (m *Manager) Driver(driverName string) Cache { 99 | return m.drivers[driverName] 100 | } 101 | 102 | // DefName get default driver name 103 | func (m *Manager) DefName() string { 104 | return m.defName 105 | } 106 | 107 | // Close all drivers 108 | func (m *Manager) Close() (err error) { 109 | for _, cache := range m.drivers { 110 | err = cache.Close() 111 | } 112 | return err 113 | } 114 | 115 | // ClearAll all drivers caches 116 | func (m *Manager) ClearAll() (err error) { 117 | for _, cache := range m.drivers { 118 | err = cache.Clear() 119 | } 120 | return err 121 | } 122 | 123 | // UnregisterAll cache drivers 124 | func (m *Manager) UnregisterAll(fn ...func(cache Cache)) int { 125 | num := len(m.drivers) 126 | 127 | // unregister 128 | m.defName = "" 129 | for name, driver := range m.drivers { 130 | if len(fn) > 0 { 131 | fn[0](driver) 132 | } 133 | 134 | delete(m.drivers, name) 135 | } 136 | return num 137 | } 138 | 139 | /************************************************************* 140 | * Quick use by default cache driver 141 | *************************************************************/ 142 | 143 | // Has cache key 144 | func (m *Manager) Has(key string) bool { 145 | return m.Default().Has(key) 146 | } 147 | 148 | // Get value by key 149 | func (m *Manager) Get(key string) any { 150 | return m.Default().Get(key) 151 | } 152 | 153 | // Set value by key 154 | func (m *Manager) Set(key string, val any, ttl time.Duration) error { 155 | return m.Default().Set(key, val, ttl) 156 | } 157 | 158 | // Del value by key 159 | func (m *Manager) Del(key string) error { 160 | return m.Default().Del(key) 161 | } 162 | 163 | // GetMulti values by keys 164 | func (m *Manager) GetMulti(keys []string) map[string]any { 165 | return m.Default().GetMulti(keys) 166 | } 167 | 168 | // SetMulti values 169 | func (m *Manager) SetMulti(mv map[string]any, ttl time.Duration) error { 170 | return m.Default().SetMulti(mv, ttl) 171 | } 172 | 173 | // DelMulti values by keys 174 | func (m *Manager) DelMulti(keys []string) error { 175 | return m.Default().DelMulti(keys) 176 | } 177 | -------------------------------------------------------------------------------- /memcached/memcached.go: -------------------------------------------------------------------------------- 1 | // Package memcached use the "github.com/bradfitz/gomemcache/memcache" as cache driver 2 | package memcached 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/bradfitz/gomemcache/memcache" 8 | "github.com/gookit/cache" 9 | ) 10 | 11 | // Name driver name 12 | const Name = "memCached" 13 | 14 | // MemCached definition 15 | type MemCached struct { 16 | cache.BaseDriver 17 | client *memcache.Client 18 | servers []string 19 | } 20 | 21 | // New a MemCached instance 22 | func New(servers ...string) *MemCached { 23 | return &MemCached{ 24 | servers: servers, 25 | } 26 | } 27 | 28 | // Connect new a MemCached instance and connect to memcached servers. 29 | func Connect(servers ...string) *MemCached { 30 | c := &MemCached{ 31 | servers: servers, 32 | } 33 | 34 | return c.Connect() 35 | } 36 | 37 | // Connect to servers 38 | func (c *MemCached) Connect() *MemCached { 39 | c.client = memcache.New(c.servers...) 40 | return c 41 | } 42 | 43 | // Has cache key 44 | func (c *MemCached) Has(key string) bool { 45 | _, err := c.client.Get(c.Key(key)) 46 | return err == nil 47 | } 48 | 49 | // Get value by key 50 | func (c *MemCached) Get(key string) (val any) { 51 | item, err := c.client.Get(c.Key(key)) 52 | if err != nil { 53 | return 54 | } 55 | 56 | err = c.UnmarshalTo(item.Value, &val) 57 | if err != nil { 58 | return nil 59 | } 60 | return 61 | } 62 | 63 | // Set value by key 64 | func (c *MemCached) Set(key string, val any, ttl time.Duration) (err error) { 65 | bts, err := c.MustMarshal(val) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | return c.client.Set(&memcache.Item{ 71 | Key: c.Key(key), 72 | Value: bts, 73 | // expire time. 0 is never expired 74 | Expiration: int32(ttl / time.Second), 75 | }) 76 | } 77 | 78 | // Del value by key 79 | func (c *MemCached) Del(key string) error { 80 | return c.client.Delete(c.Key(key)) 81 | } 82 | 83 | // GetMulti values by multi key 84 | func (c *MemCached) GetMulti(keys []string) map[string]any { 85 | keys = c.BuildKeys(keys) 86 | 87 | items, err := c.client.GetMulti(keys) 88 | if err != nil { 89 | return nil 90 | } 91 | 92 | values := make(map[string]any, len(keys)) 93 | for key, item := range items { 94 | var val any 95 | if err := c.UnmarshalTo(item.Value, &val); err != nil { 96 | continue 97 | } 98 | 99 | values[key] = val 100 | } 101 | 102 | return values 103 | } 104 | 105 | // SetMulti values by multi key 106 | func (c *MemCached) SetMulti(values map[string]any, ttl time.Duration) (err error) { 107 | for key, val := range values { 108 | if err = c.Set(c.Key(key), val, ttl); err != nil { 109 | return 110 | } 111 | } 112 | 113 | return 114 | } 115 | 116 | // DelMulti values by multi key 117 | func (c *MemCached) DelMulti(keys []string) error { 118 | for _, key := range c.BuildKeys(keys) { 119 | if err := c.client.Delete(key); err != nil { 120 | return err 121 | } 122 | } 123 | 124 | return nil 125 | } 126 | 127 | // Clear all caches 128 | func (c *MemCached) Clear() error { 129 | return c.client.DeleteAll() 130 | } 131 | 132 | // Close driver 133 | func (c *MemCached) Close() error { 134 | c.client = nil 135 | return nil 136 | } 137 | 138 | // Client get 139 | func (c *MemCached) Client() *memcache.Client { 140 | return c.client 141 | } 142 | -------------------------------------------------------------------------------- /memcached/memcached_test.go: -------------------------------------------------------------------------------- 1 | package memcached_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gookit/cache/memcached" 7 | ) 8 | 9 | func Example() { 10 | c := memcached.Connect("10.0.0.1:11211", "10.0.0.2:11211") 11 | 12 | // set 13 | err := c.Set("name", "cache value", 60) 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | // get 19 | val := c.Get("name") 20 | // del 21 | err = c.Del("name") 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | // get: "cache value" 27 | fmt.Print(val) 28 | } 29 | -------------------------------------------------------------------------------- /nutsdb/nutsdb.go: -------------------------------------------------------------------------------- 1 | // Package nutsdb use the https://github.com/xujiajun/nutsdb as cache driver 2 | package nutsdb 3 | 4 | import ( 5 | "time" 6 | ) 7 | 8 | // Name driver name 9 | const Name = "nutsdb" 10 | 11 | // NutsDB definition TODO 12 | type NutsDB struct { 13 | // cache.BaseDriver 14 | } 15 | 16 | func (c *NutsDB) Has(key string) bool { 17 | panic("implement me") 18 | } 19 | 20 | func (c *NutsDB) Get(key string) any { 21 | panic("implement me") 22 | } 23 | 24 | func (c *NutsDB) Set(key string, val any, ttl time.Duration) (err error) { 25 | panic("implement me") 26 | } 27 | 28 | func (c *NutsDB) Del(key string) error { 29 | panic("implement me") 30 | } 31 | 32 | func (c *NutsDB) GetMulti(keys []string) map[string]any { 33 | panic("implement me") 34 | } 35 | 36 | func (c *NutsDB) SetMulti(values map[string]any, ttl time.Duration) (err error) { 37 | panic("implement me") 38 | } 39 | 40 | func (c *NutsDB) DelMulti(keys []string) error { 41 | panic("implement me") 42 | } 43 | 44 | func (c *NutsDB) Clear() error { 45 | panic("implement me") 46 | } 47 | 48 | func (c *NutsDB) Close() error { 49 | panic("implement me") 50 | } 51 | -------------------------------------------------------------------------------- /redis/redigo.go: -------------------------------------------------------------------------------- 1 | // Package redis is a simple redis cache implement. 2 | // base on the package: https://github.com/gomodule/redigo 3 | package redis 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "time" 9 | 10 | "github.com/gomodule/redigo/redis" 11 | "github.com/gookit/cache" 12 | ) 13 | 14 | // Name driver name 15 | const Name = "redigo" 16 | 17 | // RedisCache fallback alias 18 | type RedisCache = Redigo 19 | 20 | // Redigo driver definition. 21 | // redigo doc link: https://pkg.go.dev/github.com/gomodule/redigo/redis#pkg-examples 22 | type Redigo struct { 23 | cache.BaseDriver 24 | // redis connection pool 25 | pool *redis.Pool 26 | // info 27 | url string 28 | pwd string 29 | dbNum int 30 | } 31 | 32 | // New redis cache 33 | func New(url, pwd string, dbNum int) *Redigo { 34 | rc := &Redigo{ 35 | url: url, pwd: pwd, dbNum: dbNum, 36 | } 37 | 38 | return rc 39 | } 40 | 41 | // Connect create and connect to redis server 42 | func Connect(url, pwd string, dbNum int) *Redigo { 43 | return New(url, pwd, dbNum).Connect() 44 | } 45 | 46 | // Connect to redis server 47 | func (c *Redigo) Connect() *Redigo { 48 | c.pool = newPool(c.url, c.pwd, c.dbNum) 49 | c.Logf("connect to server %s db is %d", c.url, c.dbNum) 50 | 51 | return c 52 | } 53 | 54 | /************************************************************* 55 | * methods implements of the gsr.SimpleCacher 56 | *************************************************************/ 57 | 58 | // Get value by key 59 | func (c *Redigo) Get(key string) any { 60 | bts, err := redis.Bytes(c.exec("Get", c.Key(key))) 61 | 62 | return c.Unmarshal(bts, err) 63 | } 64 | 65 | // GetAs get cache and unmarshal to ptr 66 | func (c *Redigo) GetAs(key string, ptr any) error { 67 | bts, err := redis.Bytes(c.exec("Get", c.Key(key))) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | return c.UnmarshalTo(bts, ptr) 73 | } 74 | 75 | // Set value by key 76 | func (c *Redigo) Set(key string, val any, ttl time.Duration) (err error) { 77 | val, err = c.Marshal(val) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | _, err = c.exec("SetEx", c.Key(key), int64(ttl/time.Second), val) 83 | return 84 | } 85 | 86 | // Del value by key 87 | func (c *Redigo) Del(key string) (err error) { 88 | _, err = c.exec("Del", c.Key(key)) 89 | return 90 | } 91 | 92 | // Has cache key 93 | func (c *Redigo) Has(key string) bool { 94 | // return 0 OR 1 95 | one, err := redis.Int(c.exec("Exists", c.Key(key))) 96 | c.SetLastErr(err) 97 | 98 | return one == 1 99 | } 100 | 101 | // GetMulti values by keys 102 | func (c *Redigo) GetMulti(keys []string) map[string]any { 103 | conn := c.pool.Get() 104 | defer conn.Close() 105 | 106 | args := make([]any, 0, len(keys)) 107 | for _, key := range keys { 108 | args = append(args, c.Key(key)) 109 | } 110 | 111 | list, err := redis.Values(c.exec("MGet", args...)) 112 | if err != nil { 113 | c.SetLastErr(err) 114 | return nil 115 | } 116 | 117 | values := make(map[string]any, len(keys)) 118 | for i, val := range list { 119 | values[keys[i]] = val 120 | } 121 | 122 | return values 123 | } 124 | 125 | // SetMulti values 126 | func (c *Redigo) SetMulti(values map[string]any, ttl time.Duration) (err error) { 127 | conn := c.pool.Get() 128 | defer conn.Close() 129 | 130 | // open multi 131 | err = conn.Send("Multi") 132 | if err != nil { 133 | return err 134 | } 135 | 136 | ttlSec := int64(ttl / time.Second) 137 | for key, val := range values { 138 | // bs, _ := cache.Marshal(val) 139 | conn.Send("SetEx", c.Key(key), ttlSec, val) 140 | } 141 | 142 | // do exec 143 | _, err = redis.Ints(conn.Do("Exec")) 144 | return 145 | } 146 | 147 | // DelMulti values by keys 148 | func (c *Redigo) DelMulti(keys []string) (err error) { 149 | args := make([]any, 0, len(keys)) 150 | for _, key := range keys { 151 | args = append(args, c.Key(key)) 152 | } 153 | 154 | _, err = c.exec("Del", args...) 155 | return 156 | } 157 | 158 | // Close connection 159 | func (c *Redigo) Close() error { 160 | return c.pool.Close() 161 | } 162 | 163 | // Clear all caches 164 | func (c *Redigo) Clear() error { 165 | _, err := c.exec("FlushDb") 166 | return err 167 | } 168 | 169 | /************************************************************* 170 | * helper methods 171 | *************************************************************/ 172 | 173 | // Pool get 174 | func (c *Redigo) Pool() *redis.Pool { 175 | return c.pool 176 | } 177 | 178 | // String get 179 | func (c *Redigo) String() string { 180 | pwd := "*" 181 | if c.IsDebug() { 182 | pwd = c.pwd 183 | } 184 | 185 | return fmt.Sprintf("connection info. url: %s, pwd: %s, dbNum: %d", c.url, pwd, c.dbNum) 186 | } 187 | 188 | // actually do the redis cmds, args[0] must be the key name. 189 | func (c *Redigo) exec(commandName string, args ...any) (reply any, err error) { 190 | if len(args) < 1 { 191 | return nil, errors.New("missing required arguments") 192 | } 193 | 194 | conn := c.pool.Get() 195 | defer conn.Close() 196 | 197 | if c.IsDebug() { 198 | st := time.Now() 199 | reply, err = conn.Do(commandName, args...) 200 | c.Logf( 201 | "operate redis cache. command: %s, key: %v, elapsed time: %.03f\n", 202 | commandName, args[0], time.Since(st).Seconds()*1000, 203 | ) 204 | return 205 | } 206 | 207 | return conn.Do(commandName, args...) 208 | } 209 | 210 | // create new pool 211 | func newPool(url, password string, dbNum int) *redis.Pool { 212 | return &redis.Pool{ 213 | MaxIdle: 5, 214 | // timeout 215 | IdleTimeout: 240 * time.Second, 216 | Dial: func() (redis.Conn, error) { 217 | c, err := redis.Dial("tcp", url) 218 | if err != nil { 219 | return nil, err 220 | } 221 | 222 | if password != "" { 223 | _, err := c.Do("AUTH", password) 224 | if err != nil { 225 | _ = c.Close() 226 | return nil, err 227 | } 228 | } 229 | _, _ = c.Do("SELECT", dbNum) 230 | return c, err 231 | }, 232 | TestOnBorrow: func(c redis.Conn, t time.Time) error { 233 | _, err := c.Do("PING") 234 | return err 235 | }, 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /redis/redigo_test.go: -------------------------------------------------------------------------------- 1 | package redis_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/gookit/cache" 8 | "github.com/gookit/cache/redis" 9 | "github.com/gookit/goutil/dump" 10 | "github.com/gookit/goutil/strutil" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Example() { 15 | // init driver 16 | c := redis.Connect("127.0.0.1:6379", "", 0) 17 | 18 | // set 19 | _ = c.Set("name", "cache value", 60) 20 | 21 | // get 22 | val := c.Get("name") 23 | 24 | // del 25 | _ = c.Del("name") 26 | 27 | // get: "cache value" 28 | fmt.Print(val) 29 | } 30 | 31 | var c *redis.Redigo 32 | 33 | func getC() *redis.Redigo { 34 | if c != nil { 35 | return c 36 | } 37 | 38 | c = redis.New("127.0.0.1:6379", "", 0).Connect() 39 | c.WithOptions(cache.WithPrefix("rdg"), cache.WithEncode(true)) 40 | return c 41 | } 42 | 43 | func TestRedigo_basic(t *testing.T) { 44 | c := getC() 45 | 46 | key := strutil.RandomCharsV2(12) 47 | dump.P("cache key: " + c.Key(key)) 48 | 49 | assert.False(t, c.Has(key)) 50 | 51 | err := c.Set(key, "value", cache.Seconds3) 52 | assert.NoError(t, err) 53 | 54 | assert.True(t, c.Has(key)) 55 | assert.Equal(t, "value", c.Get(key)) 56 | 57 | err = c.Del(key) 58 | assert.NoError(t, err) 59 | assert.False(t, c.Has(key)) 60 | } 61 | 62 | type user struct { 63 | Age int 64 | Name string 65 | } 66 | 67 | func TestRedigo_object(t *testing.T) { 68 | c := getC() 69 | b1 := user { 70 | Age: 12, 71 | Name: "inhere", 72 | } 73 | 74 | key := strutil.RandomCharsV2(12) 75 | dump.P("cache key: " + c.Key(key)) 76 | 77 | assert.False(t, c.Has(key)) 78 | 79 | err := c.Set(key, b1, cache.Seconds3) 80 | assert.NoError(t, err) 81 | assert.True(t, c.Has(key)) 82 | 83 | v := c.Get(key) 84 | assert.NotEmpty(t, v) 85 | 86 | dump.P(v) 87 | 88 | u2 := user{} 89 | err = c.GetAs(key, &u2) 90 | assert.NoError(t, err) 91 | dump.P(u2) 92 | assert.Equal(t, "inhere", u2.Name) 93 | 94 | err = c.Del(key) 95 | assert.NoError(t, err) 96 | assert.False(t, c.Has(key)) 97 | assert.Empty(t, c.Get(key)) 98 | } 99 | -------------------------------------------------------------------------------- /testdata/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gookit/cache/0250354242b3e7edcb367090da7ee3edb909b672/testdata/.keep --------------------------------------------------------------------------------