Skip to content

Commit

Permalink
Draft: refactor cache to use an interface (#1)
Browse files Browse the repository at this point in the history
* refactor cache to use an interface

* bump action versions

Signed-off-by: circa10a <[email protected]>

* fix api response types

Signed-off-by: circa10a <[email protected]>

---------

Signed-off-by: circa10a <[email protected]>
  • Loading branch information
circa10a authored Jul 13, 2024
1 parent 21c6d94 commit 8f502f1
Show file tree
Hide file tree
Showing 12 changed files with 375 additions and 197 deletions.
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

0 comments on commit 8f502f1

Please sign in to comment.