Skip to content

Commit

Permalink
Merge pull request #12 from logan-bobo/feat/url-cache
Browse files Browse the repository at this point in the history
feat: redis cache for GET requests to short URLs
  • Loading branch information
logan-bobo authored Jun 19, 2024
2 parents adf3adb + 81afde1 commit 0e2a126
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 8 deletions.
6 changes: 6 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,11 @@ services:
interval: 2s
timeout: 5s
retries: 10
redis:
image: 'bitnami/redis:latest'
environment:
- ALLOW_EMPTY_PASSWORD=yes
ports:
- 5003:6379
volumes:
pgdata:
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ module url-short
go 1.22.3

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/redis/go-redis/v9 v9.5.3 // indirect
golang.org/x/crypto v0.24.0 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
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/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-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
67 changes: 59 additions & 8 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (
"time"

"github.com/golang-jwt/jwt/v5"
"github.com/redis/go-redis/v9"
"golang.org/x/crypto/bcrypt"

"url-short/internal/database"
)

type apiConfig struct {
DB *database.Queries
RDB *redis.Client
JWTSecret string
}

Expand Down Expand Up @@ -64,11 +66,9 @@ type APIUserResponseNoToken struct {
}

func (apiCfg *apiConfig) healthz(w http.ResponseWriter, r *http.Request) {
payload := HealthResponse{
respondWithJSON(w, http.StatusOK, HealthResponse{
Status: "ok",
}

respondWithJSON(w, http.StatusOK, payload)
})
}

func (apiCfg *apiConfig) postLongURL(w http.ResponseWriter, r *http.Request, user database.User) {
Expand Down Expand Up @@ -125,15 +125,60 @@ func (apiCfg *apiConfig) getShortURL(w http.ResponseWriter, r *http.Request) {
return
}

row, err := apiCfg.DB.SelectURL(r.Context(), query)
cacheVal, err := apiCfg.RDB.Get(r.Context(), query).Result()

if err != nil {
switch {
case err == redis.Nil:
log.Printf("cache miss, key %s does not exists, writing to redis", query)

row, err := apiCfg.DB.SelectURL(r.Context(), query)

if err != nil {
log.Println(err)
respondWithError(w, http.StatusInternalServerError, "database error")
return
}

err = apiCfg.RDB.Set(r.Context(), query, row.LongUrl, (time.Hour * 1)).Err()

if err != nil {
log.Printf("could not write to redis cache %s", err)
}

http.Redirect(w, r, row.LongUrl, http.StatusMovedPermanently)
return

case err != nil:
log.Println(err)
respondWithError(w, http.StatusInternalServerError, "database error")

row, err := apiCfg.DB.SelectURL(r.Context(), query)

if err != nil {
log.Println(err)
respondWithError(w, http.StatusInternalServerError, "database error")
return
}

http.Redirect(w, r, row.LongUrl, http.StatusMovedPermanently)
return

case cacheVal == "":
log.Printf("key %s does not have a value", query)

row, err := apiCfg.DB.SelectURL(r.Context(), query)

if err != nil {
log.Println(err)
respondWithError(w, http.StatusInternalServerError, "database error")
return
}

http.Redirect(w, r, row.LongUrl, http.StatusMovedPermanently)
return
}

http.Redirect(w, r, row.LongUrl, http.StatusMovedPermanently)
log.Printf("cache hit for key %s", cacheVal)
http.Redirect(w, r, cacheVal, http.StatusMovedPermanently)
}

func (apiCfg *apiConfig) deleteShortURL(w http.ResponseWriter, r *http.Request, user database.User) {
Expand Down Expand Up @@ -185,6 +230,12 @@ func (apiCfg *apiConfig) putShortURL(w http.ResponseWriter, r *http.Request, use
return
}

err = apiCfg.RDB.Set(r.Context(), query, payload.LongURL, (time.Hour * 1)).Err()

if err != nil {
log.Println(err)
}

respondWithJSON(w, http.StatusOK, ShortURLUpdateResponse{
LongURL: payload.LongURL,
ShortURL: query,
Expand Down
12 changes: 12 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import (
"log"
"net/http"
"os"

"url-short/internal/database"

_ "github.com/lib/pq"
"github.com/redis/go-redis/v9"
)

func main() {
serverPort := os.Getenv("SERVER_PORT")
dbURL := os.Getenv("PG_CONN")
rdbURL := os.Getenv("RDB_CONN")
jwtSecret := os.Getenv("JWT_SECRET")

db, err := sql.Open("postgres", dbURL)
Expand All @@ -30,8 +33,17 @@ func main() {
Handler: mux,
}

opt, err := redis.ParseURL(rdbURL)

if err != nil {
log.Fatal(err)
}

redisClient := redis.NewClient(opt)

apiCfg := apiConfig{
DB: dbQueries,
RDB: redisClient,
JWTSecret: jwtSecret,
}

Expand Down

0 comments on commit 0e2a126

Please sign in to comment.