Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: refactor cache to use an interface #1

Merged
merged 3 commits into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Unshallow
run: git fetch --prune --unshallow
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --rm-dist
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: GoReportCard
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
with:
fetch-depth: '0'
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
- name: Bump version and push tag
uses: anothrNick/[email protected]
id: tagging
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Test
run: make test
golangci-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ By default, the library will use an in-memory cache that will be used to reduce

### Persistent

If you need a persistent cache to live outside of your application, [Redis](https://redis.io/) is supported by this library. To have the library cache address proximity using a Redis instance, simply provide a `redis.RedisOptions` struct to `geofence.Config.RedisOptions`. If `RedisOptions` is configured, the in-memory cache will not be used.
If you need a persistent cache to live outside of your application, [Redis](https://redis.io/) is supported by this library. To have the library cache address proximity using a Redis instance, simply provide a `RedisOptions` struct using the `cache` package to `geofence.Config.RedisOptions`. If `RedisOptions` is configured, the in-memory cache will not be used.

> Note: Only Redis 7 is currently supported at the time of this writing.

Expand All @@ -76,7 +76,7 @@ import (
"time"

"github.com/circa10a/go-geofence"
"github.com/go-redis/redis/v9"
geofencecache "github.com/circa10a/go-geofence/cache"
)

func main() {
Expand All @@ -87,7 +87,7 @@ func main() {
AllowPrivateIPAddresses: true,
CacheTTL: 7 * (24 * time.Hour), // 1 week
// Use Redis for caching
RedisOptions: &redis.Options{
RedisOptions: &geofencecache.RedisOptions{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
Expand All @@ -100,6 +100,7 @@ func main() {
if err != nil {
log.Fatal(err)
}

// Address nearby: false
fmt.Println("Address nearby: ", isAddressNearby)
}
Expand Down
11 changes: 11 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cache

import (
"context"
)

// Cache is an interface for caching ip addresses
type Cache interface {
Get(context.Context, string) (bool, bool, error)
Set(context.Context, string, bool) error
}
45 changes: 45 additions & 0 deletions cache/memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cache

import (
"context"
"time"

gocache "github.com/patrickmn/go-cache"
)

const (
deleteExpiredCacheItemsInternal = 10 * time.Minute
)

// MemoryCache is used to store/fetch ip proximity from an in-memory cache.
type MemoryCache struct {
memoryClient *gocache.Cache
memoryOptions *MemoryOptions
}

// MemoryOptions holds in-memory cache configuration parameters.
type MemoryOptions struct {
TTL time.Duration
}

// NewRedisCache provides a new in-memory cache client.
func NewMemoryCache(memoryOptions *MemoryOptions) *MemoryCache {
return &MemoryCache{
memoryClient: gocache.New(memoryOptions.TTL, deleteExpiredCacheItemsInternal),
memoryOptions: memoryOptions,
}
}

// Get gets value from the in-memory cache.
func (m *MemoryCache) Get(ctx context.Context, key string) (bool, bool, error) {
if isIPAddressNear, found := m.memoryClient.Get(key); found {
return isIPAddressNear.(bool), found, nil
}
return false, false, nil
}

// Set sets k/v in the in-memory cache.
func (m *MemoryCache) Set(ctx context.Context, key string, value bool) error {
m.memoryClient.Set(key, value, m.memoryOptions.TTL)
return nil
}
59 changes: 59 additions & 0 deletions cache/redis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cache

import (
"context"
"strconv"
"time"

"github.com/redis/go-redis/v9"
)

// RedisCache is used to store/fetch ip proximity from redis.
type RedisCache struct {
redisClient *redis.Client
redisOptions *RedisOptions
}

// RedisOptions holds redis configuration parameters.
type RedisOptions struct {
Addr string
Password string
DB int
TTL time.Duration
}

// NewRedisCache provides a new redis cache client.
func NewRedisCache(redisOpts *RedisOptions) *RedisCache {
return &RedisCache{
redisClient: redis.NewClient(&redis.Options{
Addr: redisOpts.Addr,
Password: redisOpts.Password,
DB: redisOpts.DB,
}),
redisOptions: redisOpts,
}
}

// Get gets value from redis.
func (r *RedisCache) Get(ctx context.Context, key string) (bool, bool, error) {
val, err := r.redisClient.Get(ctx, key).Result()
if err != nil {
// If key is not in redis
if err == redis.Nil {
return false, false, nil
}
return false, false, err
}
isIPAddressNear, err := strconv.ParseBool(val)
if err != nil {
return false, false, err
}

return isIPAddressNear, true, nil
}

// Set sets k/v in redis.
func (r *RedisCache) Set(ctx context.Context, key string, value bool) error {
// Redis stores false as 0 for whatever reason, so we'll store as a string and parse it out
return r.redisClient.Set(ctx, key, strconv.FormatBool(value), r.redisOptions.TTL).Err()
}
Loading
Loading