Skip to content

Commit 29c12f7

Browse files
authored
Merge pull request #16 from database-playground/pan93412/dbp-62-implement-apq-of-graphql
feat(graphql): implement GraphQL APQ based on Redis
2 parents a90b31a + 391a72b commit 29c12f7

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

cmd/backend/dependencies.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"entgo.io/contrib/entgql"
12+
"github.com/99designs/gqlgen/graphql"
1213
"github.com/99designs/gqlgen/graphql/handler"
1314
"github.com/99designs/gqlgen/graphql/handler/extension"
1415
"github.com/99designs/gqlgen/graphql/handler/lru"
@@ -21,6 +22,7 @@ import (
2122
"github.com/database-playground/backend-v2/internal/auth"
2223
"github.com/database-playground/backend-v2/internal/config"
2324
"github.com/database-playground/backend-v2/internal/events"
25+
"github.com/database-playground/backend-v2/internal/graphql/apq"
2426
"github.com/database-playground/backend-v2/internal/httputils"
2527
"github.com/database-playground/backend-v2/internal/sqlrunner"
2628
"github.com/database-playground/backend-v2/internal/submission"
@@ -44,8 +46,20 @@ func SqlRunner(cfg config.Config) *sqlrunner.SqlRunner {
4446
return sqlrunner.NewSqlRunner(cfg.SqlRunner)
4547
}
4648

49+
func ApqCache(redisClient rueidis.Client) graphql.Cache[string] {
50+
return apq.NewCache(redisClient, 24*time.Hour)
51+
}
52+
4753
// GqlgenHandler creates a gqlgen handler.
48-
func GqlgenHandler(entClient *ent.Client, storage auth.Storage, sqlrunner *sqlrunner.SqlRunner, useraccount *useraccount.Context, eventService *events.EventService, submissionService *submission.SubmissionService) *handler.Server {
54+
func GqlgenHandler(
55+
entClient *ent.Client,
56+
storage auth.Storage,
57+
sqlrunner *sqlrunner.SqlRunner,
58+
useraccount *useraccount.Context,
59+
eventService *events.EventService,
60+
submissionService *submission.SubmissionService,
61+
apqCache graphql.Cache[string],
62+
) *handler.Server {
4963
srv := handler.New(graph.NewSchema(entClient, storage, sqlrunner, useraccount, eventService, submissionService))
5064

5165
srv.AddTransport(transport.Options{})
@@ -57,7 +71,7 @@ func GqlgenHandler(entClient *ent.Client, storage auth.Storage, sqlrunner *sqlru
5771
srv.Use(entgql.Transactioner{TxOpener: entClient})
5872
srv.Use(extension.Introspection{})
5973
srv.Use(extension.AutomaticPersistedQuery{
60-
Cache: lru.New[string](100),
74+
Cache: apqCache,
6175
})
6276

6377
srv.SetErrorPresenter(graph.NewErrorPresenter())
@@ -86,7 +100,12 @@ func AuthService(entClient *ent.Client, storage auth.Storage, config config.Conf
86100
}
87101

88102
// GinEngine creates a gin engine.
89-
func GinEngine(services []httpapi.Service, authStorage auth.Storage, gqlgenHandler *handler.Server, cfg config.Config) *gin.Engine {
103+
func GinEngine(
104+
services []httpapi.Service,
105+
authStorage auth.Storage,
106+
gqlgenHandler *handler.Server,
107+
cfg config.Config,
108+
) *gin.Engine {
90109
engine := gin.New()
91110

92111
if err := engine.SetTrustedProxies(cfg.TrustProxies); err != nil {

cmd/backend/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func main() {
1919
UserAccountContext,
2020
EventService,
2121
SubmissionService,
22+
ApqCache,
2223
AnnotateService(AuthService),
2324
GqlgenHandler,
2425
fx.Annotate(

internal/graphql/apq/apq.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Package apq implements Apollo Client's Automatic Persisted Queries.
2+
// https://gqlgen.com/reference/apq/
3+
package apq
4+
5+
import (
6+
"context"
7+
"log/slog"
8+
"time"
9+
10+
"github.com/99designs/gqlgen/graphql"
11+
"github.com/redis/rueidis"
12+
)
13+
14+
type Cache struct {
15+
client rueidis.Client
16+
ttl time.Duration
17+
}
18+
19+
const redisApqPrefix = "apq:"
20+
21+
func NewCache(client rueidis.Client, ttl time.Duration) *Cache {
22+
return &Cache{client: client, ttl: ttl}
23+
}
24+
25+
func (c *Cache) Get(ctx context.Context, query string) (string, bool) {
26+
reply, err := c.client.Do(ctx, c.client.B().Get().Key(redisApqPrefix+query).Build()).ToString()
27+
if err != nil {
28+
if rueidis.IsRedisNil(err) {
29+
return "", false
30+
}
31+
32+
slog.Warn("error getting apq from cache", "error", err, "query", query)
33+
return "", false
34+
}
35+
36+
return reply, true
37+
}
38+
39+
func (c *Cache) Add(ctx context.Context, query string, value string) {
40+
err := c.client.Do(ctx, c.client.B().Set().Key(redisApqPrefix+query).Value(value).Ex(c.ttl).Build()).Error()
41+
if err != nil {
42+
slog.Warn("error adding apq to cache", "error", err, "query", query)
43+
}
44+
}
45+
46+
var _ graphql.Cache[string] = &Cache{}

0 commit comments

Comments
 (0)