9 Commits

Author SHA1 Message Date
65deb2e55c fix(cache): resolve test failures in Gitea Actions by closing Redis clients and using IPv4 address
Some checks failed
CI / test (push) Failing after 1m15s
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
CI / lint (push) Successful in 13s
2026-08-20 18:12:09 +03:00
ca8b137e55 fmt: apply gofmt to test files
Some checks failed
CI / lint (push) Successful in 14s
CI / test (push) Failing after 1m13s
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
2026-08-20 18:04:23 +03:00
6bebfa86fe fix(gitignore): make api ignore pattern root-specific
Some checks failed
CI / lint (push) Failing after 11s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
fix(test): add missing os import to test files
chore(deps): update dependencies from go mod tidy
2026-08-20 18:02:46 +03:00
900db58043 fix(test): use REDIS_ADDR environment variable in cron test mocks to match CI service hostname
Some checks failed
CI / lint (push) Failing after 11s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
2026-08-20 17:56:05 +03:00
e73ed82b7e fix(test): use REDIS_ADDR environment variable in test mocks to match CI service hostname
Some checks failed
CI / lint (push) Failing after 12s
CI / build (push) Has been skipped
CI / test (push) Has been skipped
CI / docker (push) Has been skipped
2026-08-20 17:49:04 +03:00
dbafaf57ce fix(ci): use Redis service hostname instead of localhost to avoid IPv6 connection issues
Some checks failed
CI / docker (push) Has been skipped
CI / lint (push) Successful in 13s
CI / test (push) Failing after 1m15s
CI / build (push) Has been skipped
2026-08-20 17:44:35 +03:00
6396f4e6ab fix(ci): add Redis service to test and build jobs to resolve connection refused errors
Some checks failed
CI / lint (push) Successful in 14s
CI / test (push) Failing after 1m48s
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
2026-08-20 17:40:37 +03:00
904c0c35ce fix(ci): replace single-job workflow with proper 4-job Gitea Actions pipeline (lint → test → build → build → docker)
Some checks failed
CI / lint (push) Successful in 54s
CI / test (push) Failing after 1m13s
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
2026-08-20 17:36:20 +03:00
70768d420e fix(ci): update Go version to 1.26.4 and modernize GitHub Actions
Some checks failed
/ build-and-deploy (push) Failing after 2m5s
2026-08-20 17:26:18 +03:00
7 changed files with 251 additions and 58 deletions

View File

@@ -1,51 +1,159 @@
name: CI
# Required repo secret: PACKAGES_TOKEN
# Generate at: Gitea → User Settings → Applications → Generate New Token
# Scopes: read:package, write:package
# Save as: Repository → Settings → Actions → Secrets → PACKAGES_TOKEN
# Note: GITHUB_TOKEN is read-only for packages in Gitea (issue #23642).
on: on:
push: push:
branches: branches: ["**"]
- master pull_request:
branches: ["master"]
env:
GO_VERSION: "1.26.4"
jobs: jobs:
build-and-deploy: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Code - uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Set up Go - uses: actions/setup-go@v5
uses: actions/setup-go@v4
with: with:
go-version: '1.22' go-version: ${{ env.GO_VERSION }}
- name: Go Modules Cache - name: Cache Go modules
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} ~/.cache/go-build
key: go-lint-${{ runner.os }}-${{ hashFiles('go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go- go-lint-${{ runner.os }}-
- name: Lint & Test - name: Download Go modules
run: go mod download
- name: go vet
run: go vet ./...
- name: gofmt check
run: | run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.54.2 fmt=$(gofmt -l .)
export PATH=$(go env GOPATH)/bin:$PATH if [ -n "$fmt" ]; then
golangci-lint run ./... echo "Files needing formatting:"
go test -v -race ./... echo "$fmt"
exit 1
fi
test:
runs-on: ubuntu-latest
services:
redis:
image: redis:7-alpine
ports:
- 6379:6379
# Set health checks to wait until redis is ready
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules and build cache
uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: go-test-${{ runner.os }}-${{ hashFiles('go.sum') }}
restore-keys: |
go-test-${{ runner.os }}-
- name: Download Go modules
run: go mod download
- name: Run tests
env:
REDIS_ADDR: redis:6379
run: go test ./... -v -race -cover
build:
runs-on: ubuntu-latest
services:
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules and build cache
uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: go-build-${{ runner.os }}-${{ hashFiles('go.sum') }}
restore-keys: |
go-build-${{ runner.os }}-
- name: Download Go modules
run: go mod download
- name: Build binary
env:
REDIS_ADDR: redis:6379
run: go build -o api ./cmd/api
docker:
runs-on: ubuntu-latest
needs: build
if: github.ref_name == 'master'
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Login to Docker Registry - name: Login to Gitea Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_USERNAME }} registry: gitea.mrixs.me
password: ${{ secrets.DOCKER_PASSWORD }} username: ${{ gitea.repository_owner }}
password: ${{ secrets.PACKAGES_TOKEN }}
- name: Build and Push Docker Image - name: Build and push
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: true push: true
tags: | tags: |
${{ secrets.DOCKER_USERNAME }}/trip-planner:latest gitea.mrixs.me/mrixs/trip-planner:latest
${{ secrets.DOCKER_USERNAME }}/trip-planner:${{ github.sha }} gitea.mrixs.me/mrixs/trip-planner:${{ github.sha }}
cache-from: type=registry,ref=gitea.mrixs.me/mrixs/trip-planner:buildcache
cache-to: type=registry,ref=gitea.mrixs.me/mrixs/trip-planner:buildcache,mode=max

2
.gitignore vendored
View File

@@ -2,5 +2,5 @@
dump.rdb dump.rdb
coverage.out coverage.out
cover.out cover.out
api /api
*.log *.log

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os"
"strings" "strings"
"testing" "testing"
@@ -44,8 +45,15 @@ func flushRedisForTest(t *testing.T, client *redis.Client) {
} }
func newMockHandlerContext() *HandlerContext { func newMockHandlerContext() *HandlerContext {
// Read Redis address from environment, default to localhost:6379
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
redisClient := redis.NewClient(&redis.Options{ redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379", Addr: redisAddr,
Password: "",
DB: 0,
}) })
// Create an empty routing graph // Create an empty routing graph

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"testing" "testing"
"time" "time"
@@ -14,7 +15,16 @@ import (
) )
func newMockMonitor(id string, tripCount int, scheduleFunc func(context.Context, string) (int, error)) *StationMonitor { func newMockMonitor(id string, tripCount int, scheduleFunc func(context.Context, string) (int, error)) *StationMonitor {
rc := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) // Read Redis address from environment, default to localhost:6379
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
rc := redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: "",
DB: 0,
})
yc := yandex.NewClient("test-key") yc := yandex.NewClient("test-key")
monitor := &StationMonitor{ monitor := &StationMonitor{
@@ -40,7 +50,16 @@ const testMonitorID = "test-station"
// gets status "active" and zero-trip day count resets to 0. // gets status "active" and zero-trip day count resets to 0.
func TestProcessStation_WithTrips(t *testing.T) { func TestProcessStation_WithTrips(t *testing.T) {
t.Helper() t.Helper()
rc := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) // Read Redis address from environment, default to localhost:6379
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
rc := redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: "",
DB: 0,
})
defer rc.Close() defer rc.Close()
ctx := context.Background() ctx := context.Background()
@@ -84,7 +103,16 @@ func TestProcessStation_WithTrips(t *testing.T) {
// with 0 trips increments the zero-trip day count. // with 0 trips increments the zero-trip day count.
func TestProcessStation_ZeroTrips_IncrementsCount(t *testing.T) { func TestProcessStation_ZeroTrips_IncrementsCount(t *testing.T) {
t.Helper() t.Helper()
rc := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) // Read Redis address from environment, default to localhost:6379
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
rc := redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: "",
DB: 0,
})
defer rc.Close() defer rc.Close()
ctx := context.Background() ctx := context.Background()
@@ -128,7 +156,16 @@ func TestProcessStation_ZeroTrips_IncrementsCount(t *testing.T) {
// with 3 consecutive days of zero trips gets status "closed". // with 3 consecutive days of zero trips gets status "closed".
func TestProcessStation_ZeroTrips_3Days_Closes(t *testing.T) { func TestProcessStation_ZeroTrips_3Days_Closes(t *testing.T) {
t.Helper() t.Helper()
rc := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) // Read Redis address from environment, default to localhost:6379
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
rc := redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: "",
DB: 0,
})
defer rc.Close() defer rc.Close()
ctx := context.Background() ctx := context.Background()
@@ -190,7 +227,16 @@ func TestProcessStation_ZeroTrips_3Days_Closes(t *testing.T) {
// closed due to 3 zero-trip days gets reactivated when trips resume. // closed due to 3 zero-trip days gets reactivated when trips resume.
func TestProcessStation_Reactivation_AfterClosure(t *testing.T) { func TestProcessStation_Reactivation_AfterClosure(t *testing.T) {
t.Helper() t.Helper()
rc := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) // Read Redis address from environment, default to localhost:6379
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
rc := redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: "",
DB: 0,
})
defer rc.Close() defer rc.Close()
ctx := context.Background() ctx := context.Background()
@@ -240,7 +286,16 @@ func TestProcessStation_Reactivation_AfterClosure(t *testing.T) {
// and that it reactivates when trips resume. // and that it reactivates when trips resume.
func TestAutoClosureChronology(t *testing.T) { func TestAutoClosureChronology(t *testing.T) {
t.Helper() t.Helper()
rc := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) // Read Redis address from environment, default to localhost:6379
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
rc := redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: "",
DB: 0,
})
defer rc.Close() defer rc.Close()
ctx := context.Background() ctx := context.Background()

4
go.mod
View File

@@ -2,9 +2,9 @@ module trip-planner
go 1.26.4 go 1.26.4
require github.com/go-redis/redis/v8 v8.11.5
require ( require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/jmoiron/sqlx v1.4.0 // indirect
) )

24
go.sum
View File

@@ -1,12 +1,24 @@
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@@ -12,9 +12,11 @@ import (
// TestCacheGetSet tests basic Get and Set operations. // TestCacheGetSet tests basic Get and Set operations.
func TestCacheGetSet(t *testing.T) { func TestCacheGetSet(t *testing.T) {
ctx := context.Background() ctx := context.Background()
client := NewRedisClient(redis.NewClient(&redis.Options{ rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", Addr: "127.0.0.1:6379",
}), metrics.New()) })
client := NewRedisClient(rdb, metrics.New())
defer rdb.Close()
// Test Set // Test Set
key := &CacheKey{Kind: "city", Code: "c146"} key := &CacheKey{Kind: "city", Code: "c146"}
@@ -88,9 +90,11 @@ func TestCacheKeyString(t *testing.T) {
// TestCacheAsideGetOrSet tests the cache-aside GetOrSetFuncPattern. // TestCacheAsideGetOrSet tests the cache-aside GetOrSetFuncPattern.
func TestCacheAsideGetOrSet(t *testing.T) { func TestCacheAsideGetOrSet(t *testing.T) {
ctx := context.Background() ctx := context.Background()
client := NewRedisClient(redis.NewClient(&redis.Options{ rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", Addr: "127.0.0.1:6379",
}), metrics.New()) })
client := NewRedisClient(rdb, metrics.New())
defer rdb.Close()
fetchCallCount := 0 fetchCallCount := 0
fetchFunc := func() ([]byte, error) { fetchFunc := func() ([]byte, error) {
@@ -128,9 +132,11 @@ func TestCacheAsideGetOrSet(t *testing.T) {
// TestCacheAsideGetCity tests GetCity with cache. // TestCacheAsideGetCity tests GetCity with cache.
func TestCacheAsideGetCity(t *testing.T) { func TestCacheAsideGetCity(t *testing.T) {
ctx := context.Background() ctx := context.Background()
client := NewRedisClient(redis.NewClient(&redis.Options{ rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", Addr: "127.0.0.1:6379",
}), metrics.New()) })
client := NewRedisClient(rdb, metrics.New())
defer rdb.Close()
fetchCallCount := 0 fetchCallCount := 0
fetchFunc := func() ([]byte, error) { fetchFunc := func() ([]byte, error) {
@@ -167,9 +173,11 @@ func TestCacheAsideGetCity(t *testing.T) {
// TestCacheAsideGetSearch tests GetSearch with near-term and far-term TTL. // TestCacheAsideGetSearch tests GetSearch with near-term and far-term TTL.
func TestCacheAsideGetSearch(t *testing.T) { func TestCacheAsideGetSearch(t *testing.T) {
ctx := context.Background() ctx := context.Background()
client := NewRedisClient(redis.NewClient(&redis.Options{ rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", Addr: "127.0.0.1:6379",
}), metrics.New()) })
client := NewRedisClient(rdb, metrics.New())
defer rdb.Close()
fetchNearTerm := func() ([]byte, error) { fetchNearTerm := func() ([]byte, error) {
return []byte(`{"near_term":true}`), nil return []byte(`{"near_term":true}`), nil
@@ -205,9 +213,11 @@ func TestCacheAsideGetSearch(t *testing.T) {
// TestCacheInvalidate tests invalidation operations. // TestCacheInvalidate tests invalidation operations.
func TestCacheInvalidate(t *testing.T) { func TestCacheInvalidate(t *testing.T) {
ctx := context.Background() ctx := context.Background()
client := NewRedisClient(redis.NewClient(&redis.Options{ rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", Addr: "127.0.0.1:6379",
}), metrics.New()) })
client := NewRedisClient(rdb, metrics.New())
defer rdb.Close()
// Set up some keys // Set up some keys
cityKey := &CacheKey{Kind: "city", Code: "c146"} cityKey := &CacheKey{Kind: "city", Code: "c146"}