Skip to content

Commit 77e8ecb

Browse files
committed
more review comments
Signed-off-by: grokspawn <jordan@nimblewidget.com>
1 parent b9fca8b commit 77e8ecb

4 files changed

Lines changed: 31 additions & 26 deletions

File tree

internal/catalogd/graphql/graphql.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ func determineFieldType(value interface{}) (reflect.Kind, bool) {
334334
return reflect.TypeOf(firstElem).Kind(), true
335335
}
336336

337+
// Sampling limits for schema inference — controls how many slice elements and
338+
// distinct values are examined when inferring GraphQL types from catalog data.
337339
const maxSampleElements = 10
338340
const maxSampleValues = 10
339341

internal/catalogd/graphql/validation.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import (
88
)
99

1010
const (
11-
MaxQueryDepth = 10
12-
MaxQueryAliases = 50
13-
MaxQueryFields = 500
11+
maxQueryDepth = 10
12+
maxQueryAliases = 50
13+
maxQueryFields = 500
1414
)
1515

1616
type queryComplexity struct {
@@ -51,21 +51,21 @@ func (c *queryComplexity) walkSelectionSet(ss *ast.SelectionSet, depth int) erro
5151
if ss == nil {
5252
return nil
5353
}
54-
if depth > MaxQueryDepth {
55-
return fmt.Errorf("query exceeds maximum depth of %d", MaxQueryDepth)
54+
if depth > maxQueryDepth {
55+
return fmt.Errorf("query exceeds maximum depth of %d", maxQueryDepth)
5656
}
5757

5858
for _, sel := range ss.Selections {
5959
switch s := sel.(type) {
6060
case *ast.Field:
6161
c.fields++
62-
if c.fields > MaxQueryFields {
63-
return fmt.Errorf("query exceeds maximum field count of %d", MaxQueryFields)
62+
if c.fields > maxQueryFields {
63+
return fmt.Errorf("query exceeds maximum field count of %d", maxQueryFields)
6464
}
6565
if s.Alias != nil && s.Alias.Value != "" {
6666
c.aliases++
67-
if c.aliases > MaxQueryAliases {
68-
return fmt.Errorf("query exceeds maximum alias count of %d", MaxQueryAliases)
67+
if c.aliases > maxQueryAliases {
68+
return fmt.Errorf("query exceeds maximum alias count of %d", maxQueryAliases)
6969
}
7070
}
7171
if err := c.walkSelectionSet(s.SelectionSet, depth+1); err != nil {

internal/catalogd/graphql/validation_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ func TestValidateQueryComplexity_ParseError(t *testing.T) {
2424
}
2525

2626
func TestValidateQueryComplexity_ExceedsDepth(t *testing.T) {
27-
// Build a query that exceeds MaxQueryDepth (10)
27+
// Build a query that exceeds maxQueryDepth (10)
2828
var b strings.Builder
2929
b.WriteString("{ ")
30-
for i := 0; i <= MaxQueryDepth+1; i++ {
30+
for i := 0; i <= maxQueryDepth+1; i++ {
3131
b.WriteString(fmt.Sprintf("f%d { ", i))
3232
}
3333
b.WriteString("leaf")
34-
for i := 0; i <= MaxQueryDepth+1; i++ {
34+
for i := 0; i <= maxQueryDepth+1; i++ {
3535
b.WriteString(" }")
3636
}
3737
b.WriteString(" }")
@@ -46,14 +46,14 @@ func TestValidateQueryComplexity_ExceedsDepth(t *testing.T) {
4646
}
4747

4848
func TestValidateQueryComplexity_WithinDepthLimit(t *testing.T) {
49-
// Build a query at exactly MaxQueryDepth (should pass)
49+
// Build a query at exactly maxQueryDepth (should pass)
5050
var b strings.Builder
5151
b.WriteString("{ ")
52-
for i := 1; i < MaxQueryDepth; i++ {
52+
for i := 1; i < maxQueryDepth; i++ {
5353
b.WriteString(fmt.Sprintf("f%d { ", i))
5454
}
5555
b.WriteString("leaf")
56-
for i := 1; i < MaxQueryDepth; i++ {
56+
for i := 1; i < maxQueryDepth; i++ {
5757
b.WriteString(" }")
5858
}
5959
b.WriteString(" }")
@@ -67,7 +67,7 @@ func TestValidateQueryComplexity_WithinDepthLimit(t *testing.T) {
6767
func TestValidateQueryComplexity_ExceedsAliases(t *testing.T) {
6868
var b strings.Builder
6969
b.WriteString("{ ")
70-
for i := 0; i <= MaxQueryAliases; i++ {
70+
for i := 0; i <= maxQueryAliases; i++ {
7171
b.WriteString(fmt.Sprintf("a%d: name ", i))
7272
}
7373
b.WriteString("}")
@@ -84,7 +84,7 @@ func TestValidateQueryComplexity_ExceedsAliases(t *testing.T) {
8484
func TestValidateQueryComplexity_ExceedsFieldCount(t *testing.T) {
8585
var b strings.Builder
8686
b.WriteString("{ ")
87-
for i := 0; i <= MaxQueryFields; i++ {
87+
for i := 0; i <= maxQueryFields; i++ {
8888
b.WriteString(fmt.Sprintf("f%d ", i))
8989
}
9090
b.WriteString("}")

internal/catalogd/storage/localdir.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,19 @@ func NewLocalDirV1(rootDir string, rootURL *url.URL, enableMetasHandler MetasHan
8181
}
8282

8383
func (s *LocalDirV1) Store(ctx context.Context, catalog string, fsys fs.FS) error {
84+
s.m.Lock()
8485
catalogDir, err := s.storeAtomicSwap(ctx, catalog, fsys)
8586
if err != nil {
87+
s.m.Unlock()
8688
return err
8789
}
90+
s.m.Unlock()
8891

89-
// Pre-warm GraphQL schema cache outside the write lock.
90-
// Concurrent queries during this window get a cache miss and trigger
91-
// their own build via singleflight, which is safe — the data is on disk.
9292
if s.graphqlSvc != nil {
9393
s.graphqlSvc.InvalidateCache(catalog)
9494

9595
if _, err := s.graphqlSvc.GetSchema(ctx, catalog); err != nil {
96-
// Schema build failed — remove the catalog to maintain consistency.
97-
// Re-acquire the write lock for the rollback since it touches shared filesystem state.
96+
s.graphqlSvc.InvalidateCache(catalog)
9897
s.m.Lock()
9998
removeErr := os.RemoveAll(catalogDir)
10099
s.m.Unlock()
@@ -109,11 +108,8 @@ func (s *LocalDirV1) Store(ctx context.Context, catalog string, fsys fs.FS) erro
109108
}
110109

111110
// storeAtomicSwap writes catalog data to a temp dir and atomically swaps it
112-
// into place. Holds the write lock for the duration of the filesystem operations only.
111+
// into place. Caller must hold s.m write lock.
113112
func (s *LocalDirV1) storeAtomicSwap(ctx context.Context, catalog string, fsys fs.FS) (string, error) {
114-
s.m.Lock()
115-
defer s.m.Unlock()
116-
117113
if err := os.MkdirAll(s.RootDir, 0700); err != nil {
118114
return "", err
119115
}
@@ -330,6 +326,9 @@ func (s *LocalDirV1) NewObjectLoader(catalog string) (gql.ObjectLoader, error) {
330326
catalogPath := catalogFilePath(s.catalogDir(catalog))
331327

332328
return func(schemaName string, offset, limit int) ([]map[string]interface{}, error) {
329+
s.m.RLock()
330+
defer s.m.RUnlock()
331+
333332
sections := idx.GetSchemaSections(schemaName)
334333
if sections == nil {
335334
return nil, nil
@@ -350,8 +349,12 @@ func (s *LocalDirV1) NewObjectLoader(catalog string) (gql.ObjectLoader, error) {
350349
}
351350
defer f.Close()
352351

352+
const maxEntrySize = 16 * 1024 * 1024 // 16 MiB
353353
results := make([]map[string]interface{}, 0, len(sections))
354354
for _, sec := range sections {
355+
if sec.Length <= 0 || sec.Length > maxEntrySize {
356+
return nil, fmt.Errorf("invalid section length %d at offset %d", sec.Length, sec.Offset)
357+
}
355358
buf := make([]byte, sec.Length)
356359
if _, err := f.ReadAt(buf, sec.Offset); err != nil {
357360
return nil, fmt.Errorf("error reading section at offset %d: %w", sec.Offset, err)

0 commit comments

Comments
 (0)