Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Updated CI so that Postgres tests run against v18 which is GA and not against v13 which is EOL (https://github.com/authzed/spicedb/pull/2926)

### Fixed
- Regression introduced in 1.49.2: missing spans in ReadSchema calls (https://github.com/authzed/spicedb/pull/2947)

## [1.49.2] - 2026-03-02
### Added
- feat(query planner): add recursive direction strategies, and fix IS BFS (https://github.com/authzed/spicedb/pull/2891)
Expand Down
12 changes: 9 additions & 3 deletions internal/middleware/memoryprotection/memory_protection.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ import (
middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"go.opentelemetry.io/otel"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

log "github.com/authzed/spicedb/internal/logging"
)

var tracer = otel.Tracer("spicedb/internal/middleware/memory_protection")

// RequestsProcessed tracks requests that were processed by this middleware.
var RequestsProcessed = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "spicedb",
Expand Down Expand Up @@ -44,7 +47,7 @@ func New(usageProvider MemoryUsageProvider, name string) *MemoryProtectionMiddle
// UnaryServerInterceptor returns a unary server interceptor that rejects incoming requests is memory usage is too high
func (am *MemoryProtectionMiddleware) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
if err := am.checkAdmission(info.FullMethod); err != nil {
if err := am.checkAdmission(ctx, info.FullMethod); err != nil {
return nil, err
}

Expand All @@ -55,7 +58,7 @@ func (am *MemoryProtectionMiddleware) UnaryServerInterceptor() grpc.UnaryServerI
// StreamServerInterceptor returns a stream server interceptor that rejects incoming requests is memory usage is too high
func (am *MemoryProtectionMiddleware) StreamServerInterceptor() grpc.StreamServerInterceptor {
return func(srv any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
if err := am.checkAdmission(info.FullMethod); err != nil {
if err := am.checkAdmission(stream.Context(), info.FullMethod); err != nil {
return err
}

Expand All @@ -65,7 +68,10 @@ func (am *MemoryProtectionMiddleware) StreamServerInterceptor() grpc.StreamServe
}

// checkAdmission returns an error if the request should be denied because memory usage is too high.
func (am *MemoryProtectionMiddleware) checkAdmission(method string) error {
func (am *MemoryProtectionMiddleware) checkAdmission(ctx context.Context, method string) error {
_, span := tracer.Start(ctx, "checkMemoryUsage")
defer span.End()

accept := true
defer func() {
am.recordMetric(method, accept)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestNew(t *testing.T) {
am := New(tt.inputProvider, "name")
require.NotNil(t, am)

err := am.checkAdmission("some_method")
err := am.checkAdmission(t.Context(), "some_method")
if tt.expectReqLetThrough {
require.NoError(t, err) // if the middleware is off, every request is let through
} else {
Expand Down
5 changes: 5 additions & 0 deletions internal/middleware/perfinsights/perfinsights.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/rs/zerolog/log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"

Expand Down Expand Up @@ -69,6 +70,8 @@ var APIShapeLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
NativeHistogramBucketFactor: 1.1,
}, append([]string{"api_kind"}, allLabels...))

var tracer = otel.Tracer("spicedb/internal/middleware")

// ShapeBuilder is a function that returns a slice of strings representing the shape of the API call.
// This is used to report the shape of the API call to Prometheus.
type ShapeBuilder func() APIShapeLabels
Expand All @@ -79,6 +82,8 @@ func ObserveShapeLatency(ctx context.Context, methodName string, shape APIShapeL
}

func observeShapeLatency(ctx context.Context, metric *prometheus.HistogramVec, methodName string, shape APIShapeLabels, duration time.Duration) {
ctx, span := tracer.Start(ctx, "perfInsights.observeShapeLatency")
defer span.End()
labels := buildLabels(methodName, shape)
if len(labels) == 0 {
log.Warn().Str("method", methodName).
Expand Down
6 changes: 6 additions & 0 deletions internal/middleware/usagemetrics/usagemetrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"go.opentelemetry.io/otel"
"google.golang.org/grpc"

"github.com/authzed/authzed-go/pkg/responsemeta"
Expand All @@ -34,6 +35,8 @@ var (
Help: "Histogram of cluster dispatches performed by the instance.",
Buckets: []float64{1, 5, 10, 25, 50, 100, 250},
}, DispatchedCountLabels)

tracer = otel.Tracer("spicedb/internal/middleware")
)

type reporter struct{}
Expand All @@ -52,6 +55,9 @@ type serverReporter struct {

// PostCall is invoked after all PostMsgSend operations.
func (r *serverReporter) PostCall(_ error, _ time.Duration) {
_, span := tracer.Start(r.ctx, "usagemetrics.PostCall")
defer span.End()

responseMeta := FromContext(r.ctx)
if responseMeta == nil {
responseMeta = &dispatch.ResponseMeta{}
Expand Down
2 changes: 1 addition & 1 deletion internal/services/shared/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ func TestApplySchemaChangesOverExisting(t *testing.T) {

sr, err := rwt.ReadSchema()
require.NoError(err)
schemaText, err := sr.SchemaText()
schemaText, err := sr.SchemaText(t.Context())
require.NoError(err)
require.Equal(tc.expectedSchema, schemaText)
return nil
Expand Down
2 changes: 1 addition & 1 deletion internal/services/v1/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
return nil, ss.rewriteError(ctx, err)
}

schemaText, err := sr.SchemaText()
schemaText, err := sr.SchemaText(ctx)

Check warning on line 99 in internal/services/v1/schema.go

View check run for this annotation

Codecov / codecov/patch

internal/services/v1/schema.go#L99

Added line #L99 was not covered by tests
if err != nil {
return nil, ss.rewriteError(ctx, err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/datalayer/datalayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type DataLayer interface {
// SchemaReader groups schema read methods, accessed via RevisionedReader.ReadSchema().
type SchemaReader interface {
// SchemaText returns the full schema text.
SchemaText() (string, error)
SchemaText(ctx context.Context) (string, error)

// LookupTypeDefByName looks up a type definition by name.
LookupTypeDefByName(ctx context.Context, name string) (datastore.RevisionedTypeDefinition, bool, error)
Expand Down
6 changes: 2 additions & 4 deletions pkg/datalayer/schema_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@

// SchemaText returns the schema text at the current revision by reading all namespaces and caveats
// and generating the schema text from them.
func (l *legacySchemaReaderAdapter) SchemaText() (string, error) {
ctx := context.Background()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing this line is the actual fix


func (l *legacySchemaReaderAdapter) SchemaText(ctx context.Context) (string, error) {

Check warning on line 33 in pkg/datalayer/schema_adapter.go

View check run for this annotation

Codecov / codecov/patch

pkg/datalayer/schema_adapter.go#L33

Added line #L33 was not covered by tests
// Read all namespaces
namespaces, err := l.ListAllTypeDefinitions(ctx)
if err != nil {
Expand Down Expand Up @@ -61,7 +59,7 @@
}

// Generate schema text with proper use directives
schemaText, _, err := generator.GenerateSchemaWithCaveatTypeSet(definitions, caveatTypeSet)
schemaText, _, err := generator.GenerateSchemaWithCaveatTypeSet(ctx, definitions, caveatTypeSet)

Check warning on line 62 in pkg/datalayer/schema_adapter.go

View check run for this annotation

Codecov / codecov/patch

pkg/datalayer/schema_adapter.go#L62

Added line #L62 was not covered by tests
if err != nil {
return "", fmt.Errorf("failed to generate schema: %w", err)
}
Expand Down
12 changes: 10 additions & 2 deletions pkg/schemadsl/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package generator

import (
"bufio"
"context"
"fmt"
"maps"
"slices"
"sort"
"strings"

"go.opentelemetry.io/otel"

"github.com/authzed/spicedb/pkg/caveats"
caveattypes "github.com/authzed/spicedb/pkg/caveats/types"
"github.com/authzed/spicedb/pkg/genutil/mapz"
Expand All @@ -18,18 +21,23 @@ import (
"github.com/authzed/spicedb/pkg/spiceerrors"
)

var tracer = otel.Tracer("spicedb/pkg/schemadsl/generator")

// Ellipsis is the relation name for terminal subjects.
const Ellipsis = "..."

// MaxSingleLineCommentLength sets the maximum length for a comment to made single line.
const MaxSingleLineCommentLength = 70 // 80 - the comment parts and some padding

func GenerateSchema(definitions []compiler.SchemaDefinition) (string, bool, error) {
return GenerateSchemaWithCaveatTypeSet(definitions, caveattypes.Default.TypeSet)
return GenerateSchemaWithCaveatTypeSet(context.TODO(), definitions, caveattypes.Default.TypeSet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does TODO mean? I've never quite understood that. Should it be a .Background()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no idea 😆

}

// GenerateSchemaWithCaveatTypeSet generates a DSL view of the given schema.
func GenerateSchemaWithCaveatTypeSet(definitions []compiler.SchemaDefinition, caveatTypeSet *caveattypes.TypeSet) (string, bool, error) {
func GenerateSchemaWithCaveatTypeSet(ctx context.Context, definitions []compiler.SchemaDefinition, caveatTypeSet *caveattypes.TypeSet) (string, bool, error) {
_, span := tracer.Start(ctx, "GenerateSchemaWithCaveatTypeSet")
defer span.End()

generated := make([]string, 0, len(definitions))
flags := mapz.NewSet[string]()

Expand Down
Loading