Repository: faabiosr/cachego
Branch: main
Commit: 859aca6d0873
Files: 38
Total size: 51.9 KB
Directory structure:
gitextract_vdd2xu1x/
├── .github/
│ └── workflows/
│ └── test.yml
├── .gitignore
├── .golangci.yaml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── bolt/
│ ├── README.md
│ ├── bolt.go
│ └── bolt_test.go
├── cache.go
├── chain/
│ ├── README.md
│ ├── chain.go
│ └── chain_test.go
├── doc.go
├── docker-compose.yml
├── errors.go
├── errors_test.go
├── file/
│ ├── README.md
│ ├── file.go
│ └── file_test.go
├── go.mod
├── go.sum
├── memcached/
│ ├── README.md
│ ├── memcached.go
│ └── memcached_test.go
├── mongo/
│ ├── README.md
│ ├── mongo.go
│ └── mongo_test.go
├── redis/
│ ├── README.md
│ ├── redis.go
│ └── redis_test.go
├── sqlite3/
│ ├── README.md
│ ├── sqlite3.go
│ └── sqlite3_test.go
└── sync/
├── README.md
├── map.go
└── map_test.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/test.yml
================================================
name: test
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
jobs:
test:
strategy:
matrix:
go-version:
- '1.21.x'
- '1.22.x'
- '1.23.x'
platform: [ubuntu-latest]
name: test
runs-on: ${{ matrix.platform }}
services:
memcached:
image: memcached:alpine
ports:
- 11211:11211
redis:
image: redis:alpine
ports:
- 6379:6379
mongodb:
image: mongo:3.6
ports:
- 27017:27017
steps:
- name: checkout the code
uses: actions/checkout@v4
- name: setup go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: unshallow
run: git fetch --prune --unshallow
- name: golanci-linter
uses: golangci/golangci-lint-action@v6
with:
version: v1.64.7
- name: run unit tests
run: make test
- name: upload code coverage
uses: codecov/codecov-action@v5
if: contains(github.ref, 'main')
with:
file: ./cover.out
================================================
FILE: .gitignore
================================================
*.swp
*.swo
cover*
cache-dir
================================================
FILE: .golangci.yaml
================================================
---
run:
timeout: "240s"
output:
formats:
- format: "colored-line-number"
linters:
enable:
- gocyclo
- unconvert
- goimports
- unused
- misspell
- nakedret
- errcheck
- revive
- ineffassign
- goconst
- govet
- unparam
- gofumpt
- prealloc
- mnd
- gocritic
linters-settings:
revive:
rules:
- name: package-comments
disabled: true
issues:
exclude-use-default: false
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
By participating to this project, you agree to abide our [code of conduct](/CODE_OF_CONDUCT.md).
## Setup your machine
`cachego` is written in [Go](https://golang.org/).
Prerequisites:
* `make`
* [Go 1.21+](https://golang.org/doc/install)
Clone `cachego` from source into `$GOPATH`:
```sh
$ mkdir -p $GOPATH/src/github.com/faabiosr
$ cd $_
$ git clone git@github.com:faabiosr/cachego.git
$ cd cachego
```
Install the build and lint dependencies:
```console
$ make depend
```
A good way of making sure everything is all right is running the test suite:
```console
$ make test
```
## Formatting the code
Format the code running:
```console
$ make fmt
```
## Create a commit
Commit messages should be well formatted.
Start your commit message with the type. Choose one of the following:
`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `revert`, `add`, `remove`, `move`, `bump`, `update`, `release`
After a colon, you should give the message a title, starting with uppercase and ending without a dot.
Keep the width of the text at 72 chars.
The title must be followed with a newline, then a more detailed description.
Please reference any GitHub issues on the last line of the commit message (e.g. `See #123`, `Closes #123`, `Fixes #123`).
An example:
```
docs: Add example for --release-notes flag
I added an example to the docs of the `--release-notes` flag to make
the usage more clear. The example is an realistic use case and might
help others to generate their own changelog.
See #284
```
## Submit a pull request
Push your branch to your `cachego` fork and open a pull request against the
main branch.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Fábio da Silva Ribeiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
.DEFAULT_GOAL := test
# Clean up
clean:
@rm -fR ./vendor/ ./cover.*
.PHONY: clean
# Run tests and generates html coverage file
cover: test
@go tool cover -html=./cover.text -o ./cover.html
@test -f ./cover.out && rm ./cover.out;
.PHONY: cover
# Up the docker container for testing
docker:
@docker-compose up -d
.PHONY: docker
# Format all go files
fmt:
@gofmt -s -w -l $(shell go list -f {{.Dir}} ./...)
.PHONY: fmt
# Run linters
lint:
@golangci-lint run ./...
.PHONY: lint
# Run tests
test:
@go test -v -race -coverprofile=./cover.text -covermode=atomic $(shell go list ./...)
.PHONY: test
================================================
FILE: README.md
================================================
# Cachego
[](https://codecov.io/gh/faabiosr/cachego)
[](https://pkg.go.dev/github.com/faabiosr/cachego)
[](https://goreportcard.com/report/github.com/faabiosr/cachego)
[](https://github.com/faabiosr/cachego/blob/main/LICENSE)
Simple interface for caching
## Installation
Cachego requires Go 1.21 or later.
```
go get github.com/faabiosr/cachego
```
## Usage
```go
package main
import (
"log"
"time"
"github.com/faabiosr/cachego/sync"
)
func main() {
cache := sync.New()
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
keys := cache.FetchMulti([]string{"user_id", "user_name"})
for k, v := range keys {
log.Printf("%s: %s\n", k, v)
}
if cache.Contains("user_name") {
cache.Delete("user_name")
}
if _, err := cache.Fetch("user_name"); err != nil {
log.Printf("%v\n", err)
}
if err := cache.Flush(); err != nil {
log.Fatal(err)
}
}
```
## Supported drivers
- [Bolt](/bolt)
- [Chain](/chain)
- [File](/file)
- [Memcached](/memcached)
- [Mongo](/mongo)
- [Redis](/redis)
- [Sqlite3](/sqlite3)
- [Sync](/sync)
## Documentation
Read the full documentation at [https://pkg.go.dev/github.com/faabiosr/cachego](https://pkg.go.dev/github.com/faabiosr/cachego).
## Development
### Requirements
- Install [docker](https://docs.docker.com/install/)
- Install [docker-compose](https://docs.docker.com/compose/install/)
### Makefile
```sh
// Clean up
$ make clean
//Run tests and generates html coverage file
$ make cover
// Up the docker containers for testing
$ make docker
// Format all go files
$ make fmt
//Run linters
$ make lint
// Run tests
$ make test
```
## License
This project is released under the MIT licence. See [LICENSE](https://github.com/faabiosr/cachego/blob/main/LICENSE) for more details.
================================================
FILE: bolt/README.md
================================================
# Cachego - BoltDB driver
The drivers uses [etcd-io/bbolt](https://github.com/etcd-io/bbolt) to store the cache data.
## Usage
```go
package main
import (
"log"
"time"
bt "go.etcd.io/bbolt"
"github.com/faabiosr/cachego/bolt"
)
func main() {
db, err := bt.Open("cache.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
cache := bolt.New(db)
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: bolt/bolt.go
================================================
// Package bolt providers a cache driver that stores the cache using BoltDB.
package bolt
import (
"encoding/json"
"errors"
"time"
bt "go.etcd.io/bbolt"
"github.com/faabiosr/cachego"
)
var boltBucket = []byte("cachego")
type (
bolt struct {
db *bt.DB
}
boltContent struct {
Duration int64 `json:"duration"`
Data string `json:"data,omitempty"`
}
)
// New creates an instance of BoltDB cache
func New(db *bt.DB) cachego.Cache {
return &bolt{db}
}
func (b *bolt) read(key string) (*boltContent, error) {
var value []byte
err := b.db.View(func(tx *bt.Tx) error {
if bucket := tx.Bucket(boltBucket); bucket != nil {
value = bucket.Get([]byte(key))
return nil
}
return errors.New("bucket not found")
})
if err != nil {
return nil, err
}
content := &boltContent{}
if err := json.Unmarshal(value, content); err != nil {
return nil, err
}
if content.Duration == 0 {
return content, nil
}
if content.Duration <= time.Now().Unix() {
_ = b.Delete(key)
return nil, cachego.ErrCacheExpired
}
return content, nil
}
// Contains checks if the cached key exists into the BoltDB storage
func (b *bolt) Contains(key string) bool {
_, err := b.read(key)
return err == nil
}
// Delete the cached key from BoltDB storage
func (b *bolt) Delete(key string) error {
return b.db.Update(func(tx *bt.Tx) error {
if bucket := tx.Bucket(boltBucket); bucket != nil {
return bucket.Delete([]byte(key))
}
return errors.New("bucket not found")
})
}
// Fetch retrieves the cached value from key of the BoltDB storage
func (b *bolt) Fetch(key string) (string, error) {
content, err := b.read(key)
if err != nil {
return "", err
}
return content.Data, nil
}
// FetchMulti retrieve multiple cached values from keys of the BoltDB storage
func (b *bolt) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
for _, key := range keys {
if value, err := b.Fetch(key); err == nil {
result[key] = value
}
}
return result
}
// Flush removes all cached keys of the BoltDB storage
func (b *bolt) Flush() error {
return b.db.Update(func(tx *bt.Tx) error {
return tx.DeleteBucket(boltBucket)
})
}
// Save a value in BoltDB storage by key
func (b *bolt) Save(key string, value string, lifeTime time.Duration) error {
duration := int64(0)
if lifeTime > 0 {
duration = time.Now().Unix() + int64(lifeTime.Seconds())
}
content := &boltContent{duration, value}
data, err := json.Marshal(content)
if err != nil {
return err
}
return b.db.Update(func(tx *bt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(boltBucket)
if err != nil {
return err
}
return bucket.Put([]byte(key), data)
})
}
================================================
FILE: bolt/bolt_test.go
================================================
package bolt
import (
"fmt"
"os"
"testing"
"time"
bt "go.etcd.io/bbolt"
)
const (
testKey = "foo"
testValue = "bar"
)
func TestBolt(t *testing.T) {
dir, err := os.MkdirTemp("", t.Name())
if err != nil {
t.Fatal(err)
}
db, err := bt.Open(fmt.Sprintf("%s/cachego.db", dir), 0o600, nil)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
_ = db.Close()
_ = os.RemoveAll(dir)
})
c := New(db)
if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if _, err := c.Fetch(testKey); err == nil {
t.Errorf("fetch fail: expected an error, got %v", err)
}
_ = c.Save(testKey, testValue, 10*time.Second)
if res, _ := c.Fetch(testKey); res != testValue {
t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
}
_ = c.Save(testKey, testValue, 0)
if !c.Contains(testKey) {
t.Errorf("contains failed: the key %s should be exist", testKey)
}
_ = c.Save("bar", testValue, 0)
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := c.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if err := c.Flush(); err == nil {
t.Errorf("flush failed: expected error, got %v", err)
}
if err := c.Delete(testKey); err == nil {
t.Errorf("delete failed: expected error, got %v", err)
}
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
}
================================================
FILE: cache.go
================================================
package cachego
import (
"time"
)
type (
// Cache is the top-level cache interface
Cache interface {
// Contains check if a cached key exists
Contains(key string) bool
// Delete remove the cached key
Delete(key string) error
// Fetch retrieve the cached key value
Fetch(key string) (string, error)
// FetchMulti retrieve multiple cached keys value
FetchMulti(keys []string) map[string]string
// Flush remove all cached keys
Flush() error
// Save cache a value by key
Save(key string, value string, lifeTime time.Duration) error
}
)
================================================
FILE: chain/README.md
================================================
# Cachego - Chain driver
The chain driver deals with multiple driver at same time, it could save the key in multiple drivers
and for fetching the driver will call the first one, if fails it will try the next until fail.
## Usage
```go
package main
import (
"log"
"time"
bt "go.etcd.io/bbolt"
"github.com/faabiosr/cachego/bolt"
"github.com/faabiosr/cachego/chain"
"github.com/faabiosr/cachego/sync"
)
func main() {
db, err := bt.Open("cache.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
cache := chain.New(
bolt.New(db),
sync.New(),
)
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: chain/chain.go
================================================
// Package chain provides chaining cache drivers operations, in case of failure
// the driver try to apply using the next driver informed, until fail.
package chain
import (
"errors"
"time"
"github.com/faabiosr/cachego"
)
type (
chain struct {
drivers []cachego.Cache
}
)
// New creates an instance of Chain cache driver
func New(drivers ...cachego.Cache) cachego.Cache {
return &chain{drivers}
}
// Contains checks if the cached key exists in one of the cache storages
func (c *chain) Contains(key string) bool {
for _, driver := range c.drivers {
if driver.Contains(key) {
return true
}
}
return false
}
// Delete the cached key in all cache storages
func (c *chain) Delete(key string) error {
for _, driver := range c.drivers {
if err := driver.Delete(key); err != nil {
return err
}
}
return nil
}
// Fetch retrieves the value of one of the registred cache storages
func (c *chain) Fetch(key string) (string, error) {
for _, driver := range c.drivers {
value, err := driver.Fetch(key)
if err == nil {
return value, nil
}
}
return "", errors.New("key not found in cache chain")
}
// FetchMulti retrieves multiple cached values from one of the registred cache storages
func (c *chain) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
for _, key := range keys {
if value, err := c.Fetch(key); err == nil {
result[key] = value
}
}
return result
}
// Flush removes all cached keys of the registered cache storages
func (c *chain) Flush() error {
for _, driver := range c.drivers {
if err := driver.Flush(); err != nil {
return err
}
}
return nil
}
// Save a value in all cache storages by key
func (c *chain) Save(key string, value string, lifeTime time.Duration) error {
for _, driver := range c.drivers {
if err := driver.Save(key, value, lifeTime); err != nil {
return err
}
}
return nil
}
================================================
FILE: chain/chain_test.go
================================================
package chain
import (
"testing"
"time"
"github.com/bradfitz/gomemcache/memcache"
"github.com/faabiosr/cachego/memcached"
"github.com/faabiosr/cachego/sync"
)
const (
testKey = "foo"
testValue = "bar"
)
func TestChain(t *testing.T) {
c := New(sync.New())
if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if _, err := c.Fetch(testKey); err == nil {
t.Errorf("fetch fail: expected an error, got %v", err)
}
_ = c.Save(testKey, testValue, 10*time.Second)
if res, _ := c.Fetch(testKey); res != testValue {
t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
}
_ = c.Save(testKey, testValue, 0)
if !c.Contains(testKey) {
t.Errorf("contains failed: the key %s should be exist", testKey)
}
_ = c.Save("bar", testValue, 0)
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := c.Delete(testKey); err != nil {
t.Errorf("delete failed: expected nil, got %v", err)
}
if err := c.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
c = New(
sync.New(),
memcached.New(memcache.New("127.0.0.1:22222")),
)
if err := c.Save(testKey, testValue, 0); err == nil {
t.Errorf("save failed: expected an error, got %v", err)
}
if err := c.Delete(testKey); err == nil {
t.Errorf("delete failed: expected an error, got %v", err)
}
if err := c.Flush(); err == nil {
t.Errorf("flush failed: expected an error, got %v", err)
}
}
================================================
FILE: doc.go
================================================
// Package cachego provides a simple way to use cache drivers.
//
// # Example Usage
//
// The following is a simple example using memcached driver:
//
// import (
// "fmt"
// "github.com/faabiosr/cachego"
// "github.com/bradfitz/gomemcache/memcache"
// )
//
// func main() {
//
// cache := cachego.NewMemcached(
// memcached.New("localhost:11211"),
// )
//
// cache.Save("foo", "bar")
//
// fmt.Println(cache.Fetch("foo"))
// }
package cachego
================================================
FILE: docker-compose.yml
================================================
---
services:
memcached:
image: "memcached:alpine"
ports:
- "11211:11211"
redis:
image: "redis:alpine"
ports:
- "6379:6379"
mongodb:
image: "mongo:3.6"
ports:
- "27017:27017"
================================================
FILE: errors.go
================================================
package cachego
type err string
// Error returns the string error value.
func (e err) Error() string {
return string(e)
}
const (
// ErrCacheExpired returns an error when the cache key was expired.
ErrCacheExpired = err("cache expired")
// ErrFlush returns an error when flush fails.
ErrFlush = err("unable to flush")
// ErrSave returns an error when save fails.
ErrSave = err("unable to save")
// ErrDelete returns an error when deletion fails.
ErrDelete = err("unable to delete")
// ErrDecode returns an errors when decode fails.
ErrDecode = err("unable to decode")
)
================================================
FILE: errors_test.go
================================================
package cachego
import (
"fmt"
"testing"
)
func TestError(t *testing.T) {
expect := "failed"
er := err(expect)
if r := fmt.Sprint(er); r != expect {
t.Errorf("invalid string: expect %s, got %s", expect, r)
}
}
================================================
FILE: file/README.md
================================================
# Cachego - File driver
The driver stores the cache data in file.
## Usage
```go
package main
import (
"log"
"time"
"github.com/faabiosr/cachego/file"
)
func main() {
cache := file.New("./cache-dir/")
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: file/file.go
================================================
// Package file providers a cache driver that stores the cache content in files.
package file
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/faabiosr/cachego"
)
type (
file struct {
dir string
sync.RWMutex
}
fileContent struct {
Duration int64 `json:"duration"`
Data string `json:"data,omitempty"`
}
)
const perm = 0o666
// New creates an instance of File cache
func New(dir string) cachego.Cache {
return &file{dir: dir}
}
func (f *file) createName(key string) string {
h := sha256.New()
_, _ = h.Write([]byte(key))
hash := hex.EncodeToString(h.Sum(nil))
return filepath.Join(f.dir, fmt.Sprintf("%s.cachego", hash))
}
func (f *file) read(key string) (*fileContent, error) {
f.RLock()
defer f.RUnlock()
value, err := os.ReadFile(f.createName(key))
if err != nil {
return nil, err
}
content := &fileContent{}
if err := json.Unmarshal(value, content); err != nil {
return nil, err
}
if content.Duration == 0 {
return content, nil
}
return content, nil
}
// Contains checks if the cached key exists into the File storage
func (f *file) Contains(key string) bool {
content, err := f.read(key)
if err != nil {
return false
}
if f.isExpired(content) {
_ = f.Delete(key)
return false
}
return true
}
// Delete the cached key from File storage
func (f *file) Delete(key string) error {
f.Lock()
defer f.Unlock()
_, err := os.Stat(f.createName(key))
if err != nil && os.IsNotExist(err) {
return nil
}
return os.Remove(f.createName(key))
}
// Fetch retrieves the cached value from key of the File storage
func (f *file) Fetch(key string) (string, error) {
content, err := f.read(key)
if err != nil {
return "", err
}
if f.isExpired(content) {
_ = f.Delete(key)
return "", cachego.ErrCacheExpired
}
return content.Data, nil
}
func (f *file) isExpired(content *fileContent) bool {
return content.Duration > 0 && content.Duration <= time.Now().Unix()
}
// FetchMulti retrieve multiple cached values from keys of the File storage
func (f *file) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
for _, key := range keys {
if value, err := f.Fetch(key); err == nil {
result[key] = value
}
}
return result
}
// Flush removes all cached keys of the File storage
func (f *file) Flush() error {
f.Lock()
defer f.Unlock()
dir, err := os.Open(f.dir)
if err != nil {
return err
}
defer func() {
_ = dir.Close()
}()
names, _ := dir.Readdirnames(-1)
for _, name := range names {
_ = os.Remove(filepath.Join(f.dir, name))
}
return nil
}
// Save a value in File storage by key
func (f *file) Save(key string, value string, lifeTime time.Duration) error {
f.Lock()
defer f.Unlock()
duration := int64(0)
if lifeTime > 0 {
duration = time.Now().Unix() + int64(lifeTime.Seconds())
}
content := &fileContent{duration, value}
data, err := json.Marshal(content)
if err != nil {
return err
}
return os.WriteFile(f.createName(key), data, perm)
}
================================================
FILE: file/file_test.go
================================================
package file
import (
"os"
"testing"
"time"
)
const (
testKey = "foo"
testValue = "bar"
)
func TestFile(t *testing.T) {
dir, err := os.MkdirTemp("", t.Name())
if err != nil {
t.Fatal(err)
}
c := New(dir)
if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if _, err := c.Fetch(testKey); err == nil {
t.Errorf("fetch fail: expected an error, got %v", err)
}
_ = c.Save(testKey, testValue, 10*time.Second)
if res, _ := c.Fetch(testKey); res != testValue {
t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
}
_ = c.Save(testKey, testValue, 0)
if !c.Contains(testKey) {
t.Errorf("contains failed: the key %s should be exist", testKey)
}
_ = c.Save("bar", testValue, 0)
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := c.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
c = New("./test/")
if err := c.Save(testKey, testValue, 0); err == nil {
t.Errorf("save failed: expected an error, got %v", err)
}
if _, err := c.Fetch(testKey); err == nil {
t.Errorf("fetch failed: expected and error, got %v", err)
}
if err := c.Flush(); err == nil {
t.Errorf("flush failed: expected an error, got %v", err)
}
}
================================================
FILE: go.mod
================================================
module github.com/faabiosr/cachego
go 1.21
require (
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
github.com/mattn/go-sqlite3 v1.14.24
github.com/redis/go-redis/v9 v9.7.3
go.etcd.io/bbolt v1.3.10
go.mongodb.org/mongo-driver/v2 v2.0.1
)
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
)
================================================
FILE: go.sum
================================================
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.mongodb.org/mongo-driver/v2 v2.0.1 h1:mhB/ZJkLSv6W6LGzY7sEjpZif47+JdfEEXjlLCIv7Qc=
go.mongodb.org/mongo-driver/v2 v2.0.1/go.mod h1:w7iFnTcQDMXtdXwcvyG3xljYpoBa1ErkI0yOzbkZ9b8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: memcached/README.md
================================================
# Cachego - Memcached driver
The drivers uses [gomemcache](https://github.com/bradfitz/gomemcache) to store the cache data.
## Usage
```go
package main
import (
"log"
"time"
"github.com/bradfitz/gomemcache/memcache"
"github.com/faabiosr/cachego/memcached"
)
func main() {
cache := memcached.New(
memcache.New("localhost:11211"),
)
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: memcached/memcached.go
================================================
// Package memcached providers a cache driver that stores the cache in Memcached.
package memcached
import (
"time"
"github.com/bradfitz/gomemcache/memcache"
"github.com/faabiosr/cachego"
)
type memcached struct {
driver *memcache.Client
}
// New creates an instance of Memcached cache driver
func New(driver *memcache.Client) cachego.Cache {
return &memcached{driver}
}
// Contains checks if cached key exists in Memcached storage
func (m *memcached) Contains(key string) bool {
_, err := m.Fetch(key)
return err == nil
}
// Delete the cached key from Memcached storage
func (m *memcached) Delete(key string) error {
return m.driver.Delete(key)
}
// Fetch retrieves the cached value from key of the Memcached storage
func (m *memcached) Fetch(key string) (string, error) {
item, err := m.driver.Get(key)
if err != nil {
return "", err
}
return string(item.Value), nil
}
// FetchMulti retrieves multiple cached value from keys of the Memcached storage
func (m *memcached) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
items, err := m.driver.GetMulti(keys)
if err != nil {
return result
}
for _, i := range items {
result[i.Key] = string(i.Value)
}
return result
}
// Flush removes all cached keys of the Memcached storage
func (m *memcached) Flush() error {
return m.driver.FlushAll()
}
// Save a value in Memcached storage by key
func (m *memcached) Save(key string, value string, lifeTime time.Duration) error {
return m.driver.Set(
&memcache.Item{
Key: key,
Value: []byte(value),
Expiration: int32(lifeTime.Seconds()),
},
)
}
================================================
FILE: memcached/memcached_test.go
================================================
package memcached
import (
"net"
"testing"
"time"
"github.com/bradfitz/gomemcache/memcache"
)
const (
testKey = "foo"
testValue = "bar"
)
func TestMemcached(t *testing.T) {
address := "localhost:11211"
conn := memcache.New(address)
if _, err := net.Dial("tcp", address); err != nil {
t.Skip(err)
}
c := New(conn)
if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if _, err := c.Fetch("bar"); err == nil {
t.Errorf("fetch fail: expected an error, got %v", err)
}
_ = c.Save(testKey, testValue, 10*time.Second)
if res, _ := c.Fetch(testKey); res != testValue {
t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
}
_ = c.Save(testKey, testValue, 0)
if !c.Contains(testKey) {
t.Errorf("contains failed: the key %s should be exist", testKey)
}
_ = c.Save("bar", testValue, 0)
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := c.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
c = New(memcache.New("localhost:22222"))
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) != 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 0, len(values))
}
}
================================================
FILE: mongo/README.md
================================================
# Cachego - Mongo driver
The drivers uses [go-mgo](https://github.com/go-mgo/mgo) to store the cache data.
## Usage
```go
package main
import (
"context"
"log"
"time"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
"github.com/faabiosr/cachego/mongo"
)
func main() {
opts := options.Client().ApplyURI("mongodb://localhost:27017")
client, _ := mongo.Connect(opts)
cache := mongo.New(
client.Database("cache").Collection("cache"),
)
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: mongo/mongo.go
================================================
// Package mongo providers a cache driver that stores the cache in MongoDB.
package mongo
import (
"context"
"time"
"github.com/faabiosr/cachego"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
type (
mongoCache struct {
collection *mongo.Collection
}
mongoContent struct {
Duration int64
Key string `bson:"_id"`
Value string
}
)
// New creates an instance of Mongo cache driver
func New(collection *mongo.Collection) cachego.Cache {
return &mongoCache{collection}
}
// NewMongoDriver alias for New.
func NewMongoDriver(collection *mongo.Collection) cachego.Cache {
return New(collection)
}
func (m *mongoCache) Contains(key string) bool {
_, err := m.Fetch(key)
return err == nil
}
// Delete the cached key from Mongo storage
func (m *mongoCache) Delete(key string) error {
_, err := m.collection.DeleteOne(context.TODO(), bson.M{"_id": bson.M{"$eq": key}})
return err
}
// Fetch retrieves the cached value from key of the Mongo storage
func (m *mongoCache) Fetch(key string) (string, error) {
content := &mongoContent{}
result := m.collection.FindOne(context.TODO(), bson.M{"_id": bson.M{"$eq": key}})
if result == nil {
return "", cachego.ErrCacheExpired
}
if result.Err() != nil {
return "", result.Err()
}
err := result.Decode(&content)
if err != nil {
return "", err
}
if content.Duration == 0 {
return content.Value, nil
}
if content.Duration <= time.Now().Unix() {
_ = m.Delete(key)
return "", cachego.ErrCacheExpired
}
return content.Value, nil
}
func (m *mongoCache) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
cur, err := m.collection.Find(context.TODO(), bson.M{"_id": bson.M{"$in": keys}})
if err != nil {
return result
}
defer func() {
_ = cur.Close(context.Background())
}()
content := &mongoContent{}
for cur.Next(context.Background()) {
err := cur.Decode(content)
if err != nil {
continue
}
result[content.Key] = content.Value
}
return result
}
// Flush removes all cached keys of the Mongo storage
func (m *mongoCache) Flush() error {
_, err := m.collection.DeleteMany(context.TODO(), bson.M{})
return err
}
// Save a value in Mongo storage by key
func (m *mongoCache) Save(key string, value string, lifeTime time.Duration) error {
duration := int64(0)
if lifeTime > 0 {
duration = time.Now().Unix() + int64(lifeTime.Seconds())
}
content := &mongoContent{duration, key, value}
opts := options.Replace().SetUpsert(true)
_, err := m.collection.ReplaceOne(context.TODO(), bson.M{"_id": bson.M{"$eq": key}}, content, opts)
return err
}
================================================
FILE: mongo/mongo_test.go
================================================
package mongo
import (
"fmt"
"testing"
"time"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
const (
testKeyMongo = "foo1"
testValueMongo = "bar"
)
func TestMongo(t *testing.T) {
// Set client options
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
// Connect to MongoDB
client, err := mongo.Connect(clientOptions)
if err != nil {
t.Skip(err)
}
collection := client.Database("cache").Collection("cache")
cache := New(collection)
if err := cache.Save(testKeyMongo, testValueMongo, 1*time.Nanosecond); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if v, err := cache.Fetch(testKeyMongo); err == nil {
t.Errorf("fetch fail: expected an error, got %v value %v", err, v)
}
_ = cache.Save(testKeyMongo, testValueMongo, 10*time.Second)
if res, _ := cache.Fetch(testKeyMongo); res != testValueMongo {
t.Errorf("fetch fail, wrong value : expected %s, got %s", testValueMongo, res)
}
_ = cache.Save(testKeyMongo, testValueMongo, 0)
if !cache.Contains(testKeyMongo) {
t.Errorf("contains failed: the key %s should be exist", testKeyMongo)
}
_ = cache.Save("bar", testValueMongo, 0)
if values := cache.FetchMulti([]string{testKeyMongo, "bar"}); len(values) != 2 {
fmt.Println(values)
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := cache.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if cache.Contains(testKeyMongo) {
t.Errorf("contains failed: the key %s should not be exist", testKeyMongo)
}
}
================================================
FILE: redis/README.md
================================================
# Cachego - Redis driver
The drivers uses [go-redis](https://github.com/go-redis/redis) to store the cache data.
## Usage
```go
package main
import (
"log"
"time"
rd "github.com/redis/go-redis/v9"
"github.com/faabiosr/cachego/redis"
)
func main() {
cache := redis.New(
rd.NewClient(&rd.Options{
Addr: ":6379",
}),
)
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: redis/redis.go
================================================
// Package redis providers a cache driver that stores the cache in Redis.
package redis
import (
"context"
"time"
rd "github.com/redis/go-redis/v9"
"github.com/faabiosr/cachego"
)
type redis struct {
driver rd.Cmdable
}
// New creates an instance of Redis cache driver
func New(driver rd.Cmdable) cachego.Cache {
return &redis{driver}
}
// Contains checks if cached key exists in Redis storage
func (r *redis) Contains(key string) bool {
i, _ := r.driver.Exists(context.Background(), key).Result()
return i > 0
}
// Delete the cached key from Redis storage
func (r *redis) Delete(key string) error {
return r.driver.Del(context.Background(), key).Err()
}
// Fetch retrieves the cached value from key of the Redis storage
func (r *redis) Fetch(key string) (string, error) {
return r.driver.Get(context.Background(), key).Result()
}
// FetchMulti retrieves multiple cached value from keys of the Redis storage
func (r *redis) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
items, err := r.driver.MGet(context.Background(), keys...).Result()
if err != nil {
return result
}
for i := 0; i < len(keys); i++ {
if items[i] != nil {
result[keys[i]] = items[i].(string)
}
}
return result
}
// Flush removes all cached keys of the Redis storage
func (r *redis) Flush() error {
return r.driver.FlushAll(context.Background()).Err()
}
// Save a value in Redis storage by key
func (r *redis) Save(key string, value string, lifeTime time.Duration) error {
return r.driver.Set(context.Background(), key, value, lifeTime).Err()
}
================================================
FILE: redis/redis_test.go
================================================
package redis
import (
"net"
"testing"
"time"
rd "github.com/redis/go-redis/v9"
)
const (
testKey = "foo"
testValue = "bar"
)
func TestRedis(t *testing.T) {
conn := rd.NewClient(&rd.Options{
Addr: ":6379",
})
if _, err := net.Dial("tcp", "localhost:6379"); err != nil {
t.Skip(err)
}
c := New(conn)
if err := c.Save(testKey, testValue, 10*time.Second); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if res, _ := c.Fetch(testKey); res != testValue {
t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
}
if _, err := c.Fetch("bar"); err == nil {
t.Errorf("fetch fail: expected an error, got %v", err)
}
if !c.Contains(testKey) {
t.Errorf("contains failed: the key %s should be exist", testKey)
}
_ = c.Save("bar", testValue, 0)
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := c.Delete(testKey); err != nil {
t.Errorf("delete failed: expected nil, got %v", err)
}
if err := c.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
conn = rd.NewClient(&rd.Options{Addr: ":6380"})
c = New(conn)
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) != 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 0, len(values))
}
}
================================================
FILE: sqlite3/README.md
================================================
# Cachego - SQLite3 driver
The drivers uses [go-sqlite3](https://github.com/mattn/go-sqlite3) to store the cache data.
## Usage
```go
package main
import (
"database/sql"
"log"
"time"
_ "github.com/mattn/go-sqlite3"
"github.com/faabiosr/cachego/sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "./cache.db")
if err != nil {
log.Fatal(err)
}
cache, err := sqlite3.New(db, "cache")
if err != nil {
log.Fatal(err)
}
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: sqlite3/sqlite3.go
================================================
// Package sqlite3 providers a cache driver that stores the cache in SQLite3.
package sqlite3
import (
"database/sql"
"fmt"
"time"
"github.com/faabiosr/cachego"
)
type (
sqlite3 struct {
db *sql.DB
table string
}
)
// New creates an instance of Sqlite3 cache driver
func New(db *sql.DB, table string) (cachego.Cache, error) {
return &sqlite3{db, table}, createTable(db, table)
}
func createTable(db *sql.DB, table string) error {
stmt := `CREATE TABLE IF NOT EXISTS %s (
key text PRIMARY KEY,
value text NOT NULL,
lifetime integer NOT NULL
);`
_, err := db.Exec(fmt.Sprintf(stmt, table))
return err
}
// Contains checks if cached key exists in Sqlite3 storage
func (s *sqlite3) Contains(key string) bool {
_, err := s.Fetch(key)
return err == nil
}
// Delete the cached key from Sqlite3 storage
func (s *sqlite3) Delete(key string) error {
tx, err := s.db.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare(fmt.Sprintf(`
DELETE FROM %s
WHERE key = ?
`, s.table))
if err != nil {
return err
}
defer func() {
_ = stmt.Close()
}()
if _, err := stmt.Exec(key); err != nil {
_ = tx.Rollback()
return err
}
return tx.Commit()
}
// Fetch retrieves the cached value from key of the Sqlite3 storage
func (s *sqlite3) Fetch(key string) (string, error) {
stmt, err := s.db.Prepare(fmt.Sprintf(`
SELECT value, lifetime
FROM %s WHERE key = ?
`, s.table))
if err != nil {
return "", err
}
defer func() {
_ = stmt.Close()
}()
var value string
var lifetime int64
if err := stmt.QueryRow(key).Scan(&value, &lifetime); err != nil {
return "", err
}
if lifetime == 0 {
return value, nil
}
if lifetime <= time.Now().Unix() {
_ = s.Delete(key)
return "", cachego.ErrCacheExpired
}
return value, nil
}
// FetchMulti retrieves multiple cached value from keys of the Sqlite3 storage
func (s *sqlite3) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
for _, key := range keys {
if value, err := s.Fetch(key); err == nil {
result[key] = value
}
}
return result
}
// Flush removes all cached keys of the Sqlite3 storage
func (s *sqlite3) Flush() error {
tx, err := s.db.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare(fmt.Sprintf("DELETE FROM %s", s.table))
if err != nil {
return err
}
defer func() {
_ = stmt.Close()
}()
if _, err := stmt.Exec(); err != nil {
_ = tx.Rollback()
return err
}
return tx.Commit()
}
// Save a value in Sqlite3 storage by key
func (s *sqlite3) Save(key string, value string, lifeTime time.Duration) error {
duration := int64(0)
if lifeTime > 0 {
duration = time.Now().Unix() + int64(lifeTime.Seconds())
}
tx, err := s.db.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare(fmt.Sprintf(`
INSERT OR REPLACE INTO %s (key, value, lifetime)
VALUES (?, ?, ?)
`, s.table))
if err != nil {
return err
}
defer func() {
_ = stmt.Close()
}()
if _, err := stmt.Exec(key, value, duration); err != nil {
_ = tx.Rollback()
return err
}
return tx.Commit()
}
================================================
FILE: sqlite3/sqlite3_test.go
================================================
package sqlite3
import (
"database/sql"
"fmt"
"os"
"testing"
"time"
_ "github.com/mattn/go-sqlite3"
)
const (
testKey = "foo"
testValue = "bar"
testTable = "cache"
testDBPath = "/cache.db"
)
func TestSqlite3(t *testing.T) {
dir, err := os.MkdirTemp("", t.Name())
if err != nil {
t.Fatal(err)
}
db, err := sql.Open("sqlite3", dir+testDBPath)
if err != nil {
t.Skip(err)
}
t.Cleanup(func() {
_ = os.RemoveAll(dir)
})
c, err := New(db, testTable)
if err != nil {
t.Skip(err)
}
if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if _, err := c.Fetch(testKey); err == nil {
t.Errorf("fetch fail: expected an error, got %v", err)
}
_ = c.Save(testKey, testValue, 10*time.Second)
if res, _ := c.Fetch(testKey); res != testValue {
t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
}
_ = c.Save(testKey, testValue, 0)
if !c.Contains(testKey) {
t.Errorf("contains failed: the key %s should be exist", testKey)
}
_ = c.Save("bar", testValue, 0)
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := c.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
}
func TestSqlite3Fail(t *testing.T) {
dir, err := os.MkdirTemp("", t.Name())
if err != nil {
t.Fatal(err)
}
db, _ := sql.Open("sqlite3", dir+testDBPath)
_ = db.Close()
t.Cleanup(func() {
_ = os.RemoveAll(dir)
})
if _, err := New(db, testTable); err == nil {
t.Errorf("constructor failed: expected an error, got %v", err)
}
db, _ = sql.Open("sqlite3", testDBPath)
c, _ := New(db, testTable)
_ = db.Close()
if err := c.Save(testKey, testValue, 0); err == nil {
t.Errorf("save failed: expected an error, got %v", err)
}
if err := c.Delete(testKey); err == nil {
t.Errorf("delete failed: expected an error, got %v", err)
}
if err := c.Flush(); err == nil {
t.Errorf("flush failed: expected an error, got %v", err)
}
db, _ = sql.Open("sqlite3", testDBPath)
c, _ = New(db, testTable)
_, _ = db.Exec(fmt.Sprintf("DROP TABLE %s;", testTable))
if err := c.Save(testKey, testValue, 0); err == nil {
t.Errorf("save failed: expected an error, got %v", err)
}
if err := c.Delete(testKey); err == nil {
t.Errorf("delete failed: expected an error, got %v", err)
}
if err := c.Flush(); err == nil {
t.Errorf("flush failed: expected an error, got %v", err)
}
}
================================================
FILE: sync/README.md
================================================
# Cachego - Sync driver
The drivers uses [golang sync](https://golang.org/pkg/sync/#Map) to store the cache data.
## Usage
```go
package main
import (
"log"
"time"
"github.com/faabiosr/cachego/sync"
)
func main() {
cache := sync.New()
if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
log.Fatal(err)
}
id, err := cache.Fetch("user_id")
if err != nil {
log.Fatal(err)
}
log.Printf("user id: %s \n", id)
}
```
================================================
FILE: sync/map.go
================================================
// Package sync providers a cache driver that uses standard golang sync.Map.
package sync
import (
"errors"
"sync"
"time"
"github.com/faabiosr/cachego"
)
type (
syncMapItem struct {
data string
duration int64
}
syncMap struct {
storage *sync.Map
}
)
// New creates an instance of SyncMap cache driver
func New() cachego.Cache {
return &syncMap{&sync.Map{}}
}
func (sm *syncMap) read(key string) (*syncMapItem, error) {
v, ok := sm.storage.Load(key)
if !ok {
return nil, errors.New("key not found")
}
item := v.(*syncMapItem)
if item.duration == 0 {
return item, nil
}
if item.duration <= time.Now().Unix() {
_ = sm.Delete(key)
return nil, cachego.ErrCacheExpired
}
return item, nil
}
// Contains checks if cached key exists in SyncMap storage
func (sm *syncMap) Contains(key string) bool {
_, err := sm.Fetch(key)
return err == nil
}
// Delete the cached key from SyncMap storage
func (sm *syncMap) Delete(key string) error {
sm.storage.Delete(key)
return nil
}
// Fetch retrieves the cached value from key of the SyncMap storage
func (sm *syncMap) Fetch(key string) (string, error) {
item, err := sm.read(key)
if err != nil {
return "", err
}
return item.data, nil
}
// FetchMulti retrieves multiple cached value from keys of the SyncMap storage
func (sm *syncMap) FetchMulti(keys []string) map[string]string {
result := make(map[string]string)
for _, key := range keys {
if value, err := sm.Fetch(key); err == nil {
result[key] = value
}
}
return result
}
// Flush removes all cached keys of the SyncMap storage
func (sm *syncMap) Flush() error {
sm.storage = &sync.Map{}
return nil
}
// Save a value in SyncMap storage by key
func (sm *syncMap) Save(key string, value string, lifeTime time.Duration) error {
duration := int64(0)
if lifeTime > 0 {
duration = time.Now().Unix() + int64(lifeTime.Seconds())
}
sm.storage.Store(key, &syncMapItem{value, duration})
return nil
}
================================================
FILE: sync/map_test.go
================================================
package sync
import (
"testing"
"time"
)
const (
testKey = "foo"
testValue = "bar"
)
func TestSyncMap(t *testing.T) {
c := New()
if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
t.Errorf("save fail: expected nil, got %v", err)
}
if _, err := c.Fetch(testKey); err == nil {
t.Errorf("fetch fail: expected an error, got %v", err)
}
_ = c.Save(testKey, testValue, 10*time.Second)
if res, _ := c.Fetch(testKey); res != testValue {
t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
}
_ = c.Save(testKey, testValue, 0)
if !c.Contains(testKey) {
t.Errorf("contains failed: the key %s should be exist", testKey)
}
_ = c.Save("bar", testValue, 0)
if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
}
if err := c.Flush(); err != nil {
t.Errorf("flush failed: expected nil, got %v", err)
}
if c.Contains(testKey) {
t.Errorf("contains failed: the key %s should not be exist", testKey)
}
}
gitextract_vdd2xu1x/
├── .github/
│ └── workflows/
│ └── test.yml
├── .gitignore
├── .golangci.yaml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── bolt/
│ ├── README.md
│ ├── bolt.go
│ └── bolt_test.go
├── cache.go
├── chain/
│ ├── README.md
│ ├── chain.go
│ └── chain_test.go
├── doc.go
├── docker-compose.yml
├── errors.go
├── errors_test.go
├── file/
│ ├── README.md
│ ├── file.go
│ └── file_test.go
├── go.mod
├── go.sum
├── memcached/
│ ├── README.md
│ ├── memcached.go
│ └── memcached_test.go
├── mongo/
│ ├── README.md
│ ├── mongo.go
│ └── mongo_test.go
├── redis/
│ ├── README.md
│ ├── redis.go
│ └── redis_test.go
├── sqlite3/
│ ├── README.md
│ ├── sqlite3.go
│ └── sqlite3_test.go
└── sync/
├── README.md
├── map.go
└── map_test.go
SYMBOL INDEX (112 symbols across 19 files)
FILE: bolt/bolt.go
type bolt (line 17) | type bolt struct
method read (line 32) | func (b *bolt) read(key string) (*boltContent, error) {
method Contains (line 65) | func (b *bolt) Contains(key string) bool {
method Delete (line 71) | func (b *bolt) Delete(key string) error {
method Fetch (line 82) | func (b *bolt) Fetch(key string) (string, error) {
method FetchMulti (line 92) | func (b *bolt) FetchMulti(keys []string) map[string]string {
method Flush (line 105) | func (b *bolt) Flush() error {
method Save (line 112) | func (b *bolt) Save(key string, value string, lifeTime time.Duration) ...
type boltContent (line 21) | type boltContent struct
function New (line 28) | func New(db *bt.DB) cachego.Cache {
FILE: bolt/bolt_test.go
constant testKey (line 13) | testKey = "foo"
constant testValue (line 14) | testValue = "bar"
function TestBolt (line 17) | func TestBolt(t *testing.T) {
FILE: cache.go
type Cache (line 9) | type Cache interface
FILE: chain/chain.go
type chain (line 13) | type chain struct
method Contains (line 24) | func (c *chain) Contains(key string) bool {
method Delete (line 35) | func (c *chain) Delete(key string) error {
method Fetch (line 46) | func (c *chain) Fetch(key string) (string, error) {
method FetchMulti (line 59) | func (c *chain) FetchMulti(keys []string) map[string]string {
method Flush (line 72) | func (c *chain) Flush() error {
method Save (line 83) | func (c *chain) Save(key string, value string, lifeTime time.Duration)...
function New (line 19) | func New(drivers ...cachego.Cache) cachego.Cache {
FILE: chain/chain_test.go
constant testKey (line 13) | testKey = "foo"
constant testValue (line 14) | testValue = "bar"
function TestChain (line 17) | func TestChain(t *testing.T) {
FILE: errors.go
type err (line 3) | type err
method Error (line 6) | func (e err) Error() string {
constant ErrCacheExpired (line 12) | ErrCacheExpired = err("cache expired")
constant ErrFlush (line 15) | ErrFlush = err("unable to flush")
constant ErrSave (line 18) | ErrSave = err("unable to save")
constant ErrDelete (line 21) | ErrDelete = err("unable to delete")
constant ErrDecode (line 24) | ErrDecode = err("unable to decode")
FILE: errors_test.go
function TestError (line 8) | func TestError(t *testing.T) {
FILE: file/file.go
type file (line 18) | type file struct
method createName (line 36) | func (f *file) createName(key string) string {
method read (line 44) | func (f *file) read(key string) (*fileContent, error) {
method Contains (line 66) | func (f *file) Contains(key string) bool {
method Delete (line 80) | func (f *file) Delete(key string) error {
method Fetch (line 93) | func (f *file) Fetch(key string) (string, error) {
method isExpired (line 107) | func (f *file) isExpired(content *fileContent) bool {
method FetchMulti (line 112) | func (f *file) FetchMulti(keys []string) map[string]string {
method Flush (line 125) | func (f *file) Flush() error {
method Save (line 148) | func (f *file) Save(key string, value string, lifeTime time.Duration) ...
type fileContent (line 23) | type fileContent struct
constant perm (line 29) | perm = 0o666
function New (line 32) | func New(dir string) cachego.Cache {
FILE: file/file_test.go
constant testKey (line 10) | testKey = "foo"
constant testValue (line 11) | testValue = "bar"
function TestFile (line 14) | func TestFile(t *testing.T) {
FILE: memcached/memcached.go
type memcached (line 11) | type memcached struct
method Contains (line 21) | func (m *memcached) Contains(key string) bool {
method Delete (line 27) | func (m *memcached) Delete(key string) error {
method Fetch (line 32) | func (m *memcached) Fetch(key string) (string, error) {
method FetchMulti (line 42) | func (m *memcached) FetchMulti(keys []string) map[string]string {
method Flush (line 58) | func (m *memcached) Flush() error {
method Save (line 63) | func (m *memcached) Save(key string, value string, lifeTime time.Durat...
function New (line 16) | func New(driver *memcache.Client) cachego.Cache {
FILE: memcached/memcached_test.go
constant testKey (line 12) | testKey = "foo"
constant testValue (line 13) | testValue = "bar"
function TestMemcached (line 16) | func TestMemcached(t *testing.T) {
FILE: mongo/mongo.go
type mongoCache (line 15) | type mongoCache struct
method Contains (line 36) | func (m *mongoCache) Contains(key string) bool {
method Delete (line 42) | func (m *mongoCache) Delete(key string) error {
method Fetch (line 48) | func (m *mongoCache) Fetch(key string) (string, error) {
method FetchMulti (line 73) | func (m *mongoCache) FetchMulti(keys []string) map[string]string {
method Flush (line 98) | func (m *mongoCache) Flush() error {
method Save (line 104) | func (m *mongoCache) Save(key string, value string, lifeTime time.Dura...
type mongoContent (line 19) | type mongoContent struct
function New (line 27) | func New(collection *mongo.Collection) cachego.Cache {
function NewMongoDriver (line 32) | func NewMongoDriver(collection *mongo.Collection) cachego.Cache {
FILE: mongo/mongo_test.go
constant testKeyMongo (line 13) | testKeyMongo = "foo1"
constant testValueMongo (line 14) | testValueMongo = "bar"
function TestMongo (line 17) | func TestMongo(t *testing.T) {
FILE: redis/redis.go
type redis (line 13) | type redis struct
method Contains (line 23) | func (r *redis) Contains(key string) bool {
method Delete (line 29) | func (r *redis) Delete(key string) error {
method Fetch (line 34) | func (r *redis) Fetch(key string) (string, error) {
method FetchMulti (line 39) | func (r *redis) FetchMulti(keys []string) map[string]string {
method Flush (line 57) | func (r *redis) Flush() error {
method Save (line 62) | func (r *redis) Save(key string, value string, lifeTime time.Duration)...
function New (line 18) | func New(driver rd.Cmdable) cachego.Cache {
FILE: redis/redis_test.go
constant testKey (line 12) | testKey = "foo"
constant testValue (line 13) | testValue = "bar"
function TestRedis (line 16) | func TestRedis(t *testing.T) {
FILE: sqlite3/sqlite3.go
type sqlite3 (line 13) | type sqlite3 struct
method Contains (line 36) | func (s *sqlite3) Contains(key string) bool {
method Delete (line 42) | func (s *sqlite3) Delete(key string) error {
method Fetch (line 69) | func (s *sqlite3) Fetch(key string) (string, error) {
method FetchMulti (line 102) | func (s *sqlite3) FetchMulti(keys []string) map[string]string {
method Flush (line 115) | func (s *sqlite3) Flush() error {
method Save (line 139) | func (s *sqlite3) Save(key string, value string, lifeTime time.Duratio...
function New (line 20) | func New(db *sql.DB, table string) (cachego.Cache, error) {
function createTable (line 24) | func createTable(db *sql.DB, table string) error {
FILE: sqlite3/sqlite3_test.go
constant testKey (line 14) | testKey = "foo"
constant testValue (line 15) | testValue = "bar"
constant testTable (line 16) | testTable = "cache"
constant testDBPath (line 17) | testDBPath = "/cache.db"
function TestSqlite3 (line 20) | func TestSqlite3(t *testing.T) {
function TestSqlite3Fail (line 75) | func TestSqlite3Fail(t *testing.T) {
FILE: sync/map.go
type syncMapItem (line 13) | type syncMapItem struct
type syncMap (line 18) | type syncMap struct
method read (line 28) | func (sm *syncMap) read(key string) (*syncMapItem, error) {
method Contains (line 49) | func (sm *syncMap) Contains(key string) bool {
method Delete (line 55) | func (sm *syncMap) Delete(key string) error {
method Fetch (line 61) | func (sm *syncMap) Fetch(key string) (string, error) {
method FetchMulti (line 71) | func (sm *syncMap) FetchMulti(keys []string) map[string]string {
method Flush (line 84) | func (sm *syncMap) Flush() error {
method Save (line 90) | func (sm *syncMap) Save(key string, value string, lifeTime time.Durati...
function New (line 24) | func New() cachego.Cache {
FILE: sync/map_test.go
constant testKey (line 9) | testKey = "foo"
constant testValue (line 10) | testValue = "bar"
function TestSyncMap (line 13) | func TestSyncMap(t *testing.T) {
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (59K chars).
[
{
"path": ".github/workflows/test.yml",
"chars": 1178,
"preview": "name: test\n\non:\n push:\n branches:\n - main\n - develop\n pull_request:\n branches:\n - main\n\njobs:\n t"
},
{
"path": ".gitignore",
"chars": 29,
"preview": "*.swp\n*.swo\ncover*\ncache-dir\n"
},
{
"path": ".golangci.yaml",
"chars": 465,
"preview": "---\nrun:\n timeout: \"240s\"\n\noutput:\n formats:\n - format: \"colored-line-number\"\n\nlinters:\n enable:\n - gocyclo\n "
},
{
"path": "CONTRIBUTING.md",
"chars": 1663,
"preview": "# Contributing\n\nBy participating to this project, you agree to abide our [code of conduct](/CODE_OF_CONDUCT.md).\n\n## Set"
},
{
"path": "LICENSE",
"chars": 1089,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Fábio da Silva Ribeiro\n\nPermission is hereby granted, free of charge, to any p"
},
{
"path": "Makefile",
"chars": 604,
"preview": ".DEFAULT_GOAL := test\n\n# Clean up\nclean:\n\t@rm -fR ./vendor/ ./cover.*\n.PHONY: clean\n\n# Run tests and generates html cove"
},
{
"path": "README.md",
"chars": 2256,
"preview": "# Cachego\n\n[](http"
},
{
"path": "bolt/README.md",
"chars": 558,
"preview": "# Cachego - BoltDB driver\nThe drivers uses [etcd-io/bbolt](https://github.com/etcd-io/bbolt) to store the cache data.\n\n#"
},
{
"path": "bolt/bolt.go",
"chars": 2706,
"preview": "// Package bolt providers a cache driver that stores the cache using BoltDB.\npackage bolt\n\nimport (\n\t\"encoding/json\"\n\t\"e"
},
{
"path": "bolt/bolt_test.go",
"chars": 1541,
"preview": "package bolt\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\tbt \"go.etcd.io/bbolt\"\n)\n\nconst (\n\ttestKey = \"foo\"\n\ttestValue "
},
{
"path": "cache.go",
"chars": 567,
"preview": "package cachego\n\nimport (\n\t\"time\"\n)\n\ntype (\n\t// Cache is the top-level cache interface\n\tCache interface {\n\t\t// Contains "
},
{
"path": "chain/README.md",
"chars": 764,
"preview": "# Cachego - Chain driver\nThe chain driver deals with multiple driver at same time, it could save the key in multiple dri"
},
{
"path": "chain/chain.go",
"chars": 1912,
"preview": "// Package chain provides chaining cache drivers operations, in case of failure\n// the driver try to apply using the nex"
},
{
"path": "chain/chain_test.go",
"chars": 1693,
"preview": "package chain\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bradfitz/gomemcache/memcache\"\n\t\"github.com/faabiosr/cachego/mem"
},
{
"path": "doc.go",
"chars": 465,
"preview": "// Package cachego provides a simple way to use cache drivers.\n//\n// # Example Usage\n//\n// The following is a simple exa"
},
{
"path": "docker-compose.yml",
"chars": 249,
"preview": "---\nservices:\n memcached:\n image: \"memcached:alpine\"\n ports:\n - \"11211:11211\"\n\n redis:\n image:"
},
{
"path": "errors.go",
"chars": 588,
"preview": "package cachego\n\ntype err string\n\n// Error returns the string error value.\nfunc (e err) Error() string {\n\treturn string("
},
{
"path": "errors_test.go",
"chars": 221,
"preview": "package cachego\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestError(t *testing.T) {\n\texpect := \"failed\"\n\ter := err(expect)\n\n\ti"
},
{
"path": "file/README.md",
"chars": 413,
"preview": "# Cachego - File driver\nThe driver stores the cache data in file.\n\n## Usage\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"time\""
},
{
"path": "file/file.go",
"chars": 3061,
"preview": "// Package file providers a cache driver that stores the cache content in files.\npackage file\n\nimport (\n\t\"crypto/sha256\""
},
{
"path": "file/file_test.go",
"chars": 1488,
"preview": "package file\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\ttestKey = \"foo\"\n\ttestValue = \"bar\"\n)\n\nfunc TestFile(t *tes"
},
{
"path": "go.mod",
"chars": 871,
"preview": "module github.com/faabiosr/cachego\n\ngo 1.21\n\nrequire (\n\tgithub.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b0387"
},
{
"path": "go.sum",
"chars": 6644,
"preview": "github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=\ngithub"
},
{
"path": "memcached/README.md",
"chars": 548,
"preview": "# Cachego - Memcached driver\nThe drivers uses [gomemcache](https://github.com/bradfitz/gomemcache) to store the cache da"
},
{
"path": "memcached/memcached.go",
"chars": 1630,
"preview": "// Package memcached providers a cache driver that stores the cache in Memcached.\npackage memcached\n\nimport (\n\t\"time\"\n\n\t"
},
{
"path": "memcached/memcached_test.go",
"chars": 1444,
"preview": "package memcached\n\nimport (\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bradfitz/gomemcache/memcache\"\n)\n\nconst (\n\ttestKey "
},
{
"path": "mongo/README.md",
"chars": 697,
"preview": "# Cachego - Mongo driver\nThe drivers uses [go-mgo](https://github.com/go-mgo/mgo) to store the cache data.\n\n## Usage\n\n``"
},
{
"path": "mongo/mongo.go",
"chars": 2678,
"preview": "// Package mongo providers a cache driver that stores the cache in MongoDB.\npackage mongo\n\nimport (\n\t\"context\"\n\t\"time\"\n\n"
},
{
"path": "mongo/mongo_test.go",
"chars": 1596,
"preview": "package mongo\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver"
},
{
"path": "redis/README.md",
"chars": 538,
"preview": "# Cachego - Redis driver\nThe drivers uses [go-redis](https://github.com/go-redis/redis) to store the cache data.\n\n## Usa"
},
{
"path": "redis/redis.go",
"chars": 1588,
"preview": "// Package redis providers a cache driver that stores the cache in Redis.\npackage redis\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\tr"
},
{
"path": "redis/redis_test.go",
"chars": 1570,
"preview": "package redis\n\nimport (\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\trd \"github.com/redis/go-redis/v9\"\n)\n\nconst (\n\ttestKey = \"foo\"\n\ttes"
},
{
"path": "sqlite3/README.md",
"chars": 645,
"preview": "# Cachego - SQLite3 driver\nThe drivers uses [go-sqlite3](https://github.com/mattn/go-sqlite3) to store the cache data.\n\n"
},
{
"path": "sqlite3/sqlite3.go",
"chars": 3109,
"preview": "// Package sqlite3 providers a cache driver that stores the cache in SQLite3.\npackage sqlite3\n\nimport (\n\t\"database/sql\"\n"
},
{
"path": "sqlite3/sqlite3_test.go",
"chars": 2638,
"preview": "package sqlite3\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\nconst (\n\t"
},
{
"path": "sync/README.md",
"chars": 447,
"preview": "# Cachego - Sync driver\nThe drivers uses [golang sync](https://golang.org/pkg/sync/#Map) to store the cache data.\n\n## Us"
},
{
"path": "sync/map.go",
"chars": 1962,
"preview": "// Package sync providers a cache driver that uses standard golang sync.Map.\npackage sync\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\""
},
{
"path": "sync/map_test.go",
"chars": 1061,
"preview": "package sync\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\ttestKey = \"foo\"\n\ttestValue = \"bar\"\n)\n\nfunc TestSyncMap(t *testin"
}
]
About this extraction
This page contains the full source code of the faabiosr/cachego GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (51.9 KB), approximately 17.8k tokens, and a symbol index with 112 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.