diff --git a/internal/datastore/proxy/observable.go b/internal/datastore/proxy/observable.go index e819f7d45a..e052557c70 100644 --- a/internal/datastore/proxy/observable.go +++ b/internal/datastore/proxy/observable.go @@ -2,6 +2,7 @@ package proxy import ( "context" + "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -12,6 +13,7 @@ import ( v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" "github.com/authzed/spicedb/internal/datastore/common" + "github.com/authzed/spicedb/internal/dstrace" "github.com/authzed/spicedb/internal/telemetry/otelconv" "github.com/authzed/spicedb/pkg/datastore" "github.com/authzed/spicedb/pkg/datastore/options" @@ -230,12 +232,19 @@ func (r *observableReader) LegacyReadNamespaceByName(ctx context.Context, nsName func (r *observableReader) QueryRelationships(ctx context.Context, filter datastore.RelationshipsFilter, opts ...options.QueryOptionsOption) (datastore.RelationshipIterator, error) { queryOpts := options.NewQueryOptionsWithOptions(opts...) - ctx, closer := observe(ctx, "QueryRelationships", string(queryOpts.QueryShape), trace.WithAttributes( + queryShape := string(queryOpts.QueryShape) + ctx, closer := observe(ctx, "QueryRelationships", queryShape, trace.WithAttributes( attribute.String(otelconv.AttrDatastoreResourceType, filter.OptionalResourceType), attribute.String(otelconv.AttrDatastoreResourceRelation, filter.OptionalResourceRelation), - attribute.String(otelconv.AttrDatastoreQueryShape, string(queryOpts.QueryShape)), + attribute.String(otelconv.AttrDatastoreQueryShape, queryShape), )) + collector := dstrace.CollectorFromContext(ctx) + var start time.Time + if collector != nil { + start = time.Now() + } + iterator, err := r.delegate.QueryRelationships(ctx, filter, opts...) if err != nil { closer() @@ -253,14 +262,24 @@ func (r *observableReader) QueryRelationships(ctx context.Context, filter datast } } loadedRelationshipCount.Observe(float64(count)) + if collector != nil { + collector.Record(queryShape, start, time.Since(start), count) + } }, nil } func (r *observableReader) ReverseQueryRelationships(ctx context.Context, subjectsFilter datastore.SubjectsFilter, opts ...options.ReverseQueryOptionsOption) (datastore.RelationshipIterator, error) { queryOpts := options.NewReverseQueryOptionsWithOptions(opts...) - ctx, closer := observe(ctx, "ReverseQueryRelationships", string(queryOpts.QueryShapeForReverse), trace.WithAttributes( + queryShape := string(queryOpts.QueryShapeForReverse) + ctx, closer := observe(ctx, "ReverseQueryRelationships", queryShape, trace.WithAttributes( attribute.String(otelconv.AttrDatastoreSubjectType, subjectsFilter.SubjectType), - attribute.String(otelconv.AttrDatastoreQueryShape, string(queryOpts.QueryShapeForReverse)))) + attribute.String(otelconv.AttrDatastoreQueryShape, queryShape))) + + collector := dstrace.CollectorFromContext(ctx) + var start time.Time + if collector != nil { + start = time.Now() + } iterator, err := r.delegate.ReverseQueryRelationships(ctx, subjectsFilter, opts...) if err != nil { @@ -279,6 +298,9 @@ func (r *observableReader) ReverseQueryRelationships(ctx context.Context, subjec } } loadedRelationshipCount.Observe(float64(count)) + if collector != nil { + collector.Record(queryShape, start, time.Since(start), count) + } }, nil } diff --git a/internal/datastore/proxy/observable_test.go b/internal/datastore/proxy/observable_test.go index 60f641a503..e45e6d69a5 100644 --- a/internal/datastore/proxy/observable_test.go +++ b/internal/datastore/proxy/observable_test.go @@ -13,7 +13,10 @@ import ( "github.com/authzed/spicedb/internal/datastore/proxy/proxy_test" "github.com/authzed/spicedb/internal/datastore/revisions" + "github.com/authzed/spicedb/internal/dstrace" "github.com/authzed/spicedb/pkg/datastore" + "github.com/authzed/spicedb/pkg/datastore/options" + "github.com/authzed/spicedb/pkg/datastore/queryshape" core "github.com/authzed/spicedb/pkg/proto/core/v1" "github.com/authzed/spicedb/pkg/tuple" ) @@ -360,6 +363,90 @@ func TestObservableProxy_ReaderMethodsWithMetrics(t *testing.T) { } } +func TestObservableProxy_RecordsDatastoreQueriesToCollector(t *testing.T) { + t.Run("QueryRelationships", func(t *testing.T) { + dsMock, readerMock, _ := newMocks() + twoRelIter := datastore.RelationshipIterator(func(yield func(tuple.Relationship, error) bool) { + if !yield(tuple.MustParse("document:1#viewer@user:1"), nil) { + return + } + yield(tuple.MustParse("document:1#viewer@user:2"), nil) + }) + readerMock.On("QueryRelationships", datastore.RelationshipsFilter{OptionalResourceType: "document"}, mock.Anything). + Return(twoRelIter, nil).Once() + + sut := NewObservableDatastoreProxy(dsMock) + ctx, collector := dstrace.WithCollector(t.Context()) + + iter, err := sut.SnapshotReader(testRev).QueryRelationships( + ctx, + datastore.RelationshipsFilter{OptionalResourceType: "document"}, + options.WithQueryShape(queryshape.CheckPermissionSelectDirectSubjects), + ) + require.NoError(t, err) + for range iter { + } + + queries := collector.Queries() + require.Len(t, queries, 1) + require.Equal(t, string(queryshape.CheckPermissionSelectDirectSubjects), queries[0].QueryShape) + require.Equal(t, uint64(2), queries[0].RelationshipCount) + require.NotNil(t, queries[0].StartTime) + require.NotNil(t, queries[0].Duration) + + dsMock.AssertExpectations(t) + readerMock.AssertExpectations(t) + }) + + t.Run("ReverseQueryRelationships", func(t *testing.T) { + dsMock, readerMock, _ := newMocks() + oneRelIter := datastore.RelationshipIterator(func(yield func(tuple.Relationship, error) bool) { + yield(tuple.MustParse("document:1#viewer@user:1"), nil) + }) + readerMock.On("ReverseQueryRelationships", datastore.SubjectsFilter{SubjectType: "user"}, mock.Anything). + Return(oneRelIter, nil).Once() + + sut := NewObservableDatastoreProxy(dsMock) + ctx, collector := dstrace.WithCollector(t.Context()) + + iter, err := sut.SnapshotReader(testRev).ReverseQueryRelationships( + ctx, + datastore.SubjectsFilter{SubjectType: "user"}, + options.WithQueryShapeForReverse(queryshape.MatchingResourcesForSubject), + ) + require.NoError(t, err) + for range iter { + } + + queries := collector.Queries() + require.Len(t, queries, 1) + require.Equal(t, string(queryshape.MatchingResourcesForSubject), queries[0].QueryShape) + require.Equal(t, uint64(1), queries[0].RelationshipCount) + + dsMock.AssertExpectations(t) + readerMock.AssertExpectations(t) + }) + + t.Run("no collector is a no-op", func(t *testing.T) { + dsMock, readerMock, _ := newMocks() + iter := datastore.RelationshipIterator(func(yield func(tuple.Relationship, error) bool) { + yield(tuple.MustParse("document:1#viewer@user:1"), nil) + }) + readerMock.On("QueryRelationships", datastore.RelationshipsFilter{OptionalResourceType: "document"}). + Return(iter, nil).Once() + + sut := NewObservableDatastoreProxy(dsMock) + got, err := sut.SnapshotReader(testRev).QueryRelationships( + t.Context(), datastore.RelationshipsFilter{OptionalResourceType: "document"}) + require.NoError(t, err) + for range got { + } + + dsMock.AssertExpectations(t) + readerMock.AssertExpectations(t) + }) +} + func TestObservableProxy_RWTMethodsWithMetrics(t *testing.T) { tests := []struct { name string diff --git a/internal/dstrace/collector.go b/internal/dstrace/collector.go new file mode 100644 index 0000000000..402d5ba454 --- /dev/null +++ b/internal/dstrace/collector.go @@ -0,0 +1,71 @@ +// Package dstrace provides a context-scoped collector for attributing datastore +// query timings to a single Check dispatch node. It is populated by the +// observable datastore proxy and drained into the check debug trace, but lives +// in its own leaf package so neither side has to depend on the other. +package dstrace + +import ( + "context" + "sync" + "time" + + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" +) + +type collectorCtxKey struct{} + +// Collector accumulates datastore query observations for a single Check +// dispatch node. It is safe for concurrent use: a node may issue multiple +// relationship queries in parallel (e.g. the branches of a union). +type Collector struct { + mu sync.Mutex + queries []*v1.DatastoreQuery +} + +// WithCollector returns a context carrying a fresh Collector, along with that +// collector. It should be called once per dispatch node when debug tracing is +// enabled so that the queries the node issues itself are attributed to it; +// dispatched children re-install their own collector and therefore capture their +// own queries. +func WithCollector(ctx context.Context) (context.Context, *Collector) { + c := &Collector{} + return context.WithValue(ctx, collectorCtxKey{}, c), c +} + +// CollectorFromContext returns the Collector stored in ctx, or nil if none is +// present (i.e. debug tracing is disabled). A nil Collector is safe to call +// Record/Queries on. +func CollectorFromContext(ctx context.Context) *Collector { + c, _ := ctx.Value(collectorCtxKey{}).(*Collector) + return c +} + +// Record appends a single datastore query observation. It is a no-op on a nil +// Collector. +func (c *Collector) Record(queryShape string, start time.Time, duration time.Duration, relationshipCount uint64) { + if c == nil { + return + } + c.mu.Lock() + defer c.mu.Unlock() + c.queries = append(c.queries, &v1.DatastoreQuery{ + QueryShape: queryShape, + StartTime: timestamppb.New(start), + Duration: durationpb.New(duration), + RelationshipCount: relationshipCount, + }) +} + +// Queries returns the collected datastore query observations. It returns nil on +// a nil Collector. +func (c *Collector) Queries() []*v1.DatastoreQuery { + if c == nil { + return nil + } + c.mu.Lock() + defer c.mu.Unlock() + return c.queries +} diff --git a/internal/dstrace/collector_test.go b/internal/dstrace/collector_test.go new file mode 100644 index 0000000000..d3cc016e86 --- /dev/null +++ b/internal/dstrace/collector_test.go @@ -0,0 +1,78 @@ +package dstrace + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestCollectorFromContextAbsentIsNilSafe(t *testing.T) { + c := CollectorFromContext(context.Background()) + require.Nil(t, c) + + // A nil collector must be safe to use. + c.Record("shape", time.Now(), time.Millisecond, 1) + require.Nil(t, c.Queries()) +} + +func TestWithCollectorRecordsQueries(t *testing.T) { + ctx, c := WithCollector(context.Background()) + require.NotNil(t, c) + require.Same(t, c, CollectorFromContext(ctx)) + + start := time.Now() + c.Record("shape-a", start, 2*time.Millisecond, 3) + c.Record("shape-b", start, 5*time.Millisecond, 0) + + queries := c.Queries() + require.Len(t, queries, 2) + + require.Equal(t, "shape-a", queries[0].QueryShape) + require.Equal(t, uint64(3), queries[0].RelationshipCount) + require.Equal(t, 2*time.Millisecond, queries[0].Duration.AsDuration()) + require.WithinDuration(t, start, queries[0].StartTime.AsTime(), time.Microsecond) + + require.Equal(t, "shape-b", queries[1].QueryShape) + require.Equal(t, uint64(0), queries[1].RelationshipCount) +} + +func TestCollectorConcurrentRecord(t *testing.T) { + _, c := WithCollector(context.Background()) + + const goroutines = 16 + const perGoroutine = 64 + + var wg sync.WaitGroup + wg.Add(goroutines) + for i := 0; i < goroutines; i++ { + go func() { + defer wg.Done() + for j := 0; j < perGoroutine; j++ { + c.Record("shape", time.Now(), time.Microsecond, 1) + } + }() + } + wg.Wait() + + require.Len(t, c.Queries(), goroutines*perGoroutine) +} + +// Each child context gets its own collector, so a parent never sees a child's +// recorded queries. +func TestNestedCollectorsAreIndependent(t *testing.T) { + parentCtx, parent := WithCollector(context.Background()) + parent.Record("parent-query", time.Now(), time.Millisecond, 1) + + childCtx, child := WithCollector(parentCtx) + child.Record("child-query", time.Now(), time.Millisecond, 2) + + require.Len(t, parent.Queries(), 1) + require.Equal(t, "parent-query", parent.Queries()[0].QueryShape) + + require.Same(t, child, CollectorFromContext(childCtx)) + require.Len(t, child.Queries(), 1) + require.Equal(t, "child-query", child.Queries()[0].QueryShape) +} diff --git a/internal/graph/check.go b/internal/graph/check.go index 6d9a9938e1..6c860ca086 100644 --- a/internal/graph/check.go +++ b/internal/graph/check.go @@ -10,8 +10,10 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/authzed/spicedb/internal/dispatch" + "github.com/authzed/spicedb/internal/dstrace" "github.com/authzed/spicedb/internal/graph/hints" log "github.com/authzed/spicedb/internal/logging" "github.com/authzed/spicedb/internal/namespace" @@ -98,9 +100,14 @@ type currentRequestContext struct { // Check performs a check request with the provided request and context func (cc *ConcurrentChecker) Check(ctx context.Context, req ValidatedCheckRequest, relation *core.Relation) (*v1.DispatchCheckResponse, error) { var startTime *time.Time + var collector *dstrace.Collector if req.Debug != v1.DispatchCheckRequest_NO_DEBUG { now := time.Now() startTime = &now + // Install a per-node collector so the datastore queries this node issues + // itself are attributed to it. Dispatched children re-enter Check and + // install their own collector, so their reads attribute to them. + ctx, collector = dstrace.WithCollector(ctx) } resolved := cc.checkInternal(ctx, req, relation) @@ -131,6 +138,8 @@ func (cc *ConcurrentChecker) Check(ctx context.Context, req ValidatedCheckReques debugInfo.Check.Request = clonedRequest debugInfo.Check.Duration = durationpb.New(time.Since(*startTime)) + debugInfo.Check.StartTime = timestamppb.New(*startTime) + debugInfo.Check.DatastoreQueries = collector.Queries() if nspkg.GetRelationKind(relation) == iv1.RelationMetadata_PERMISSION { debugInfo.Check.ResourceRelationType = v1.CheckDebugTrace_PERMISSION diff --git a/internal/graph/computed/computecheck_test.go b/internal/graph/computed/computecheck_test.go index ed0faa8861..6db33222d8 100644 --- a/internal/graph/computed/computecheck_test.go +++ b/internal/graph/computed/computecheck_test.go @@ -11,6 +11,7 @@ import ( "github.com/authzed/spicedb/internal/datastore/dsfortesting" "github.com/authzed/spicedb/internal/datastore/memdb" + "github.com/authzed/spicedb/internal/datastore/proxy" "github.com/authzed/spicedb/internal/dispatch/graph" "github.com/authzed/spicedb/internal/graph/computed" log "github.com/authzed/spicedb/internal/logging" @@ -935,6 +936,89 @@ func TestComputeBulkCheck(t *testing.T) { require.Equal(t, v1.ResourceCheckResult_NOT_MEMBER, resp["third"].Membership) } +// TestComputeCheckTraceTimingAndDatastoreQueries verifies that, with trace +// debugging enabled, every node of the dispatch trace carries a wall-clock +// start_time and that the datastore reads issued during the check are attributed +// to nodes as DatastoreQuery events. The memdb datastore is wrapped in the +// observable proxy here, mirroring the production datastore chain. +func TestComputeCheckTraceTimingAndDatastoreQueries(t *testing.T) { + req := require.New(t) + + rawDS, err := dsfortesting.NewMemDBDatastoreForTesting(t, 0, 0, memdb.DisableGC) + req.NoError(err) + ds := proxy.NewObservableDatastoreProxy(rawDS) + + dispatch, err := graph.NewLocalOnlyDispatcher(graph.MustNewDefaultDispatcherParametersForTesting()) + req.NoError(err) + ctx := log.Logger.WithContext(datalayer.ContextWithHandle(t.Context())) + req.NoError(datalayer.SetInContext(ctx, datalayer.NewDataLayer(ds))) + + revision, err := writeCaveatedTuples(ctx, t, ds, ` + definition user {} + + definition group { + relation member: user + } + + definition document { + relation viewer: user | group#member + permission view = viewer + } + `, []caveatedUpdate{ + {tuple.UpdateOperationCreate, "document:foo#viewer@group:eng#member", "", nil}, + {tuple.UpdateOperationCreate, "group:eng#member@user:tom", "", nil}, + }) + req.NoError(err) + + results, _, debugInfos, err := computed.ComputeBulkCheck(ctx, dispatch, + caveattypes.Default.TypeSet, + computed.CheckParameters{ + ResourceType: tuple.RR("document", "view"), + Subject: tuple.ONR("user", "tom", "..."), + AtRevision: revision, + MaximumDepth: 50, + DebugOption: computed.TraceDebuggingEnabled, + SchemaHash: datalayer.NoSchemaHashForTesting, + }, + []string{"foo"}, + 100, + ) + req.NoError(err) + req.Equal(v1.ResourceCheckResult_MEMBER, results["foo"].Membership) + req.NotEmpty(debugInfos) + + var nodeCount, queryCount int + var walk func(tr *v1.CheckDebugTrace) + walk = func(tr *v1.CheckDebugTrace) { + if tr == nil { + return + } + nodeCount++ + + // Cached nodes have no request and don't re-stamp timing; skip them. + if !tr.IsCachedResult && tr.Request != nil { + req.NotNil(tr.StartTime, "every executed check node must carry a start_time") + } + + for _, q := range tr.DatastoreQueries { + queryCount++ + req.NotEmpty(q.QueryShape) + req.NotNil(q.StartTime, "datastore query must have a start_time") + req.NotNil(q.Duration, "datastore query must have a duration") + } + + for _, sp := range tr.SubProblems { + walk(sp) + } + } + for _, di := range debugInfos { + walk(di.Check) + } + + req.Positive(nodeCount) + req.Positive(queryCount, "expected at least one datastore query attributed in the trace") +} + func writeCaveatedTuples(ctx context.Context, _ *testing.T, ds datastore.Datastore, schema string, updates []caveatedUpdate) (datastore.Revision, error) { compiled, err := compiler.Compile(compiler.InputSchema{ Source: "schema", diff --git a/pkg/proto/dispatch/v1/dispatch.pb.go b/pkg/proto/dispatch/v1/dispatch.pb.go index 7f79ecafe7..eb548a1058 100644 --- a/pkg/proto/dispatch/v1/dispatch.pb.go +++ b/pkg/proto/dispatch/v1/dispatch.pb.go @@ -1641,10 +1641,14 @@ type CheckDebugTrace struct { IsCachedResult bool `protobuf:"varint,4,opt,name=is_cached_result,json=isCachedResult,proto3" json:"is_cached_result,omitempty"` SubProblems []*CheckDebugTrace `protobuf:"bytes,5,rep,name=sub_problems,json=subProblems,proto3" json:"sub_problems,omitempty"` Duration *durationpb.Duration `protobuf:"bytes,6,opt,name=duration,proto3" json:"duration,omitempty"` - TraceId string `protobuf:"bytes,7,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` - SourceId string `protobuf:"bytes,8,opt,name=source_id,json=sourceId,proto3" json:"source_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // start_time is the wall-clock start of this Check; end = start_time + duration. + StartTime *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + TraceId string `protobuf:"bytes,7,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` + SourceId string `protobuf:"bytes,8,opt,name=source_id,json=sourceId,proto3" json:"source_id,omitempty"` + // datastore_queries are the relationship queries this node issued itself. + DatastoreQueries []*DatastoreQuery `protobuf:"bytes,10,rep,name=datastore_queries,json=datastoreQueries,proto3" json:"datastore_queries,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CheckDebugTrace) Reset() { @@ -1719,6 +1723,13 @@ func (x *CheckDebugTrace) GetDuration() *durationpb.Duration { return nil } +func (x *CheckDebugTrace) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + func (x *CheckDebugTrace) GetTraceId() string { if x != nil { return x.TraceId @@ -1733,6 +1744,82 @@ func (x *CheckDebugTrace) GetSourceId() string { return "" } +func (x *CheckDebugTrace) GetDatastoreQueries() []*DatastoreQuery { + if x != nil { + return x.DatastoreQueries + } + return nil +} + +// DatastoreQuery records a single relationship query issued while resolving a Check node. +type DatastoreQuery struct { + state protoimpl.MessageState `protogen:"open.v1"` + QueryShape string `protobuf:"bytes,1,opt,name=query_shape,json=queryShape,proto3" json:"query_shape,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + Duration *durationpb.Duration `protobuf:"bytes,3,opt,name=duration,proto3" json:"duration,omitempty"` + RelationshipCount uint64 `protobuf:"varint,4,opt,name=relationship_count,json=relationshipCount,proto3" json:"relationship_count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DatastoreQuery) Reset() { + *x = DatastoreQuery{} + mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DatastoreQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DatastoreQuery) ProtoMessage() {} + +func (x *DatastoreQuery) ProtoReflect() protoreflect.Message { + mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DatastoreQuery.ProtoReflect.Descriptor instead. +func (*DatastoreQuery) Descriptor() ([]byte, []int) { + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{21} +} + +func (x *DatastoreQuery) GetQueryShape() string { + if x != nil { + return x.QueryShape + } + return "" +} + +func (x *DatastoreQuery) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *DatastoreQuery) GetDuration() *durationpb.Duration { + if x != nil { + return x.Duration + } + return nil +} + +func (x *DatastoreQuery) GetRelationshipCount() uint64 { + if x != nil { + return x.RelationshipCount + } + return 0 +} + type PlanContext struct { state protoimpl.MessageState `protogen:"open.v1"` Revision string `protobuf:"bytes,1,opt,name=revision,proto3" json:"revision,omitempty"` @@ -1761,7 +1848,7 @@ type PlanContext struct { func (x *PlanContext) Reset() { *x = PlanContext{} - mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1773,7 +1860,7 @@ func (x *PlanContext) String() string { func (*PlanContext) ProtoMessage() {} func (x *PlanContext) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[21] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1786,7 +1873,7 @@ func (x *PlanContext) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanContext.ProtoReflect.Descriptor instead. func (*PlanContext) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{21} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{22} } func (x *PlanContext) GetRevision() string { @@ -1860,7 +1947,7 @@ type DispatchQueryPlanRequest struct { func (x *DispatchQueryPlanRequest) Reset() { *x = DispatchQueryPlanRequest{} - mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1872,7 +1959,7 @@ func (x *DispatchQueryPlanRequest) String() string { func (*DispatchQueryPlanRequest) ProtoMessage() {} func (x *DispatchQueryPlanRequest) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[22] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1885,7 +1972,7 @@ func (x *DispatchQueryPlanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchQueryPlanRequest.ProtoReflect.Descriptor instead. func (*DispatchQueryPlanRequest) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{22} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{23} } func (x *DispatchQueryPlanRequest) GetOperation() PlanOperation { @@ -1940,7 +2027,7 @@ type DispatchQueryPlanResponse struct { func (x *DispatchQueryPlanResponse) Reset() { *x = DispatchQueryPlanResponse{} - mi := &file_dispatch_v1_dispatch_proto_msgTypes[23] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1952,7 +2039,7 @@ func (x *DispatchQueryPlanResponse) String() string { func (*DispatchQueryPlanResponse) ProtoMessage() {} func (x *DispatchQueryPlanResponse) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[23] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1965,7 +2052,7 @@ func (x *DispatchQueryPlanResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DispatchQueryPlanResponse.ProtoReflect.Descriptor instead. func (*DispatchQueryPlanResponse) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{23} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{24} } func (x *DispatchQueryPlanResponse) GetMetadata() *ResponseMeta { @@ -2001,7 +2088,7 @@ type ResultPath struct { func (x *ResultPath) Reset() { *x = ResultPath{} - mi := &file_dispatch_v1_dispatch_proto_msgTypes[24] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2013,7 +2100,7 @@ func (x *ResultPath) String() string { func (*ResultPath) ProtoMessage() {} func (x *ResultPath) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[24] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2026,7 +2113,7 @@ func (x *ResultPath) ProtoReflect() protoreflect.Message { // Deprecated: Use ResultPath.ProtoReflect.Descriptor instead. func (*ResultPath) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{24} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{25} } func (x *ResultPath) GetResourceType() string { @@ -2120,7 +2207,7 @@ type LookupDebugInfo struct { func (x *LookupDebugInfo) Reset() { *x = LookupDebugInfo{} - mi := &file_dispatch_v1_dispatch_proto_msgTypes[25] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2132,7 +2219,7 @@ func (x *LookupDebugInfo) String() string { func (*LookupDebugInfo) ProtoMessage() {} func (x *LookupDebugInfo) ProtoReflect() protoreflect.Message { - mi := &file_dispatch_v1_dispatch_proto_msgTypes[25] + mi := &file_dispatch_v1_dispatch_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2145,7 +2232,7 @@ func (x *LookupDebugInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use LookupDebugInfo.ProtoReflect.Descriptor instead. func (*LookupDebugInfo) Descriptor() ([]byte, []int) { - return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{25} + return file_dispatch_v1_dispatch_proto_rawDescGZIP(), []int{26} } func (x *LookupDebugInfo) GetCycleMembers() []string { @@ -2296,16 +2383,20 @@ const file_dispatch_v1_dispatch_proto_rawDesc = "" + "\n" + "debug_info\x18\x06 \x01(\v2\x1d.dispatch.v1.DebugInformationR\tdebugInfoJ\x04\b\x04\x10\x05J\x04\b\x05\x10\x06\"F\n" + "\x10DebugInformation\x122\n" + - "\x05check\x18\x01 \x01(\v2\x1c.dispatch.v1.CheckDebugTraceR\x05check\"\xe7\x04\n" + + "\x05check\x18\x01 \x01(\v2\x1c.dispatch.v1.CheckDebugTraceR\x05check\"\xec\x05\n" + "\x0fCheckDebugTrace\x12;\n" + "\arequest\x18\x01 \x01(\v2!.dispatch.v1.DispatchCheckRequestR\arequest\x12_\n" + "\x16resource_relation_type\x18\x02 \x01(\x0e2).dispatch.v1.CheckDebugTrace.RelationTypeR\x14resourceRelationType\x12C\n" + "\aresults\x18\x03 \x03(\v2).dispatch.v1.CheckDebugTrace.ResultsEntryR\aresults\x12(\n" + "\x10is_cached_result\x18\x04 \x01(\bR\x0eisCachedResult\x12?\n" + "\fsub_problems\x18\x05 \x03(\v2\x1c.dispatch.v1.CheckDebugTraceR\vsubProblems\x125\n" + - "\bduration\x18\x06 \x01(\v2\x19.google.protobuf.DurationR\bduration\x12\x19\n" + + "\bduration\x18\x06 \x01(\v2\x19.google.protobuf.DurationR\bduration\x129\n" + + "\n" + + "start_time\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x12\x19\n" + "\btrace_id\x18\a \x01(\tR\atraceId\x12\x1b\n" + - "\tsource_id\x18\b \x01(\tR\bsourceId\x1a\\\n" + + "\tsource_id\x18\b \x01(\tR\bsourceId\x12H\n" + + "\x11datastore_queries\x18\n" + + " \x03(\v2\x1b.dispatch.v1.DatastoreQueryR\x10datastoreQueries\x1a\\\n" + "\fResultsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x126\n" + "\x05value\x18\x02 \x01(\v2 .dispatch.v1.ResourceCheckResultR\x05value:\x028\x01\"9\n" + @@ -2313,7 +2404,14 @@ const file_dispatch_v1_dispatch_proto_rawDesc = "" + "\aUNKNOWN\x10\x00\x12\f\n" + "\bRELATION\x10\x01\x12\x0e\n" + "\n" + - "PERMISSION\x10\x02\"\xea\x02\n" + + "PERMISSION\x10\x02\"\xd2\x01\n" + + "\x0eDatastoreQuery\x12\x1f\n" + + "\vquery_shape\x18\x01 \x01(\tR\n" + + "queryShape\x129\n" + + "\n" + + "start_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x125\n" + + "\bduration\x18\x03 \x01(\v2\x19.google.protobuf.DurationR\bduration\x12-\n" + + "\x12relationship_count\x18\x04 \x01(\x04R\x11relationshipCount\"\xea\x02\n" + "\vPlanContext\x12\x1a\n" + "\brevision\x18\x01 \x01(\tR\brevision\x12>\n" + "\x0ecaveat_context\x18\x02 \x01(\v2\x17.google.protobuf.StructR\rcaveatContext\x12.\n" + @@ -2382,7 +2480,7 @@ func file_dispatch_v1_dispatch_proto_rawDescGZIP() []byte { } var file_dispatch_v1_dispatch_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_dispatch_v1_dispatch_proto_msgTypes = make([]protoimpl.MessageInfo, 29) +var file_dispatch_v1_dispatch_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_dispatch_v1_dispatch_proto_goTypes = []any{ (PlanOperation)(0), // 0: dispatch.v1.PlanOperation (DispatchCheckRequest_DebugSetting)(0), // 1: dispatch.v1.DispatchCheckRequest.DebugSetting @@ -2411,106 +2509,111 @@ var file_dispatch_v1_dispatch_proto_goTypes = []any{ (*ResponseMeta)(nil), // 24: dispatch.v1.ResponseMeta (*DebugInformation)(nil), // 25: dispatch.v1.DebugInformation (*CheckDebugTrace)(nil), // 26: dispatch.v1.CheckDebugTrace - (*PlanContext)(nil), // 27: dispatch.v1.PlanContext - (*DispatchQueryPlanRequest)(nil), // 28: dispatch.v1.DispatchQueryPlanRequest - (*DispatchQueryPlanResponse)(nil), // 29: dispatch.v1.DispatchQueryPlanResponse - (*ResultPath)(nil), // 30: dispatch.v1.ResultPath - (*LookupDebugInfo)(nil), // 31: dispatch.v1.LookupDebugInfo - nil, // 32: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry - nil, // 33: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry - nil, // 34: dispatch.v1.CheckDebugTrace.ResultsEntry - (*v1.RelationReference)(nil), // 35: core.v1.RelationReference - (*v1.ObjectAndRelation)(nil), // 36: core.v1.ObjectAndRelation - (*v1.CaveatExpression)(nil), // 37: core.v1.CaveatExpression - (*v1.RelationTupleTreeNode)(nil), // 38: core.v1.RelationTupleTreeNode - (*structpb.Struct)(nil), // 39: google.protobuf.Struct - (*durationpb.Duration)(nil), // 40: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 41: google.protobuf.Timestamp - (*v1.RelationshipIntegrity)(nil), // 42: core.v1.RelationshipIntegrity + (*DatastoreQuery)(nil), // 27: dispatch.v1.DatastoreQuery + (*PlanContext)(nil), // 28: dispatch.v1.PlanContext + (*DispatchQueryPlanRequest)(nil), // 29: dispatch.v1.DispatchQueryPlanRequest + (*DispatchQueryPlanResponse)(nil), // 30: dispatch.v1.DispatchQueryPlanResponse + (*ResultPath)(nil), // 31: dispatch.v1.ResultPath + (*LookupDebugInfo)(nil), // 32: dispatch.v1.LookupDebugInfo + nil, // 33: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry + nil, // 34: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry + nil, // 35: dispatch.v1.CheckDebugTrace.ResultsEntry + (*v1.RelationReference)(nil), // 36: core.v1.RelationReference + (*v1.ObjectAndRelation)(nil), // 37: core.v1.ObjectAndRelation + (*v1.CaveatExpression)(nil), // 38: core.v1.CaveatExpression + (*v1.RelationTupleTreeNode)(nil), // 39: core.v1.RelationTupleTreeNode + (*structpb.Struct)(nil), // 40: google.protobuf.Struct + (*durationpb.Duration)(nil), // 41: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 42: google.protobuf.Timestamp + (*v1.RelationshipIntegrity)(nil), // 43: core.v1.RelationshipIntegrity } var file_dispatch_v1_dispatch_proto_depIdxs = []int32{ 23, // 0: dispatch.v1.DispatchCheckRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 35, // 1: dispatch.v1.DispatchCheckRequest.resource_relation:type_name -> core.v1.RelationReference - 36, // 2: dispatch.v1.DispatchCheckRequest.subject:type_name -> core.v1.ObjectAndRelation + 36, // 1: dispatch.v1.DispatchCheckRequest.resource_relation:type_name -> core.v1.RelationReference + 37, // 2: dispatch.v1.DispatchCheckRequest.subject:type_name -> core.v1.ObjectAndRelation 2, // 3: dispatch.v1.DispatchCheckRequest.results_setting:type_name -> dispatch.v1.DispatchCheckRequest.ResultsSetting 1, // 4: dispatch.v1.DispatchCheckRequest.debug:type_name -> dispatch.v1.DispatchCheckRequest.DebugSetting 7, // 5: dispatch.v1.DispatchCheckRequest.check_hints:type_name -> dispatch.v1.CheckHint - 36, // 6: dispatch.v1.CheckHint.resource:type_name -> core.v1.ObjectAndRelation - 36, // 7: dispatch.v1.CheckHint.subject:type_name -> core.v1.ObjectAndRelation + 37, // 6: dispatch.v1.CheckHint.resource:type_name -> core.v1.ObjectAndRelation + 37, // 7: dispatch.v1.CheckHint.subject:type_name -> core.v1.ObjectAndRelation 9, // 8: dispatch.v1.CheckHint.result:type_name -> dispatch.v1.ResourceCheckResult 24, // 9: dispatch.v1.DispatchCheckResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 32, // 10: dispatch.v1.DispatchCheckResponse.results_by_resource_id:type_name -> dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry + 33, // 10: dispatch.v1.DispatchCheckResponse.results_by_resource_id:type_name -> dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry 3, // 11: dispatch.v1.ResourceCheckResult.membership:type_name -> dispatch.v1.ResourceCheckResult.Membership - 37, // 12: dispatch.v1.ResourceCheckResult.expression:type_name -> core.v1.CaveatExpression + 38, // 12: dispatch.v1.ResourceCheckResult.expression:type_name -> core.v1.CaveatExpression 23, // 13: dispatch.v1.DispatchExpandRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 36, // 14: dispatch.v1.DispatchExpandRequest.resource_and_relation:type_name -> core.v1.ObjectAndRelation + 37, // 14: dispatch.v1.DispatchExpandRequest.resource_and_relation:type_name -> core.v1.ObjectAndRelation 4, // 15: dispatch.v1.DispatchExpandRequest.expansion_mode:type_name -> dispatch.v1.DispatchExpandRequest.ExpansionMode 24, // 16: dispatch.v1.DispatchExpandResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 38, // 17: dispatch.v1.DispatchExpandResponse.tree_node:type_name -> core.v1.RelationTupleTreeNode + 39, // 17: dispatch.v1.DispatchExpandResponse.tree_node:type_name -> core.v1.RelationTupleTreeNode 23, // 18: dispatch.v1.DispatchLookupResources2Request.metadata:type_name -> dispatch.v1.ResolverMeta - 35, // 19: dispatch.v1.DispatchLookupResources2Request.resource_relation:type_name -> core.v1.RelationReference - 35, // 20: dispatch.v1.DispatchLookupResources2Request.subject_relation:type_name -> core.v1.RelationReference - 36, // 21: dispatch.v1.DispatchLookupResources2Request.terminal_subject:type_name -> core.v1.ObjectAndRelation - 39, // 22: dispatch.v1.DispatchLookupResources2Request.context:type_name -> google.protobuf.Struct + 36, // 19: dispatch.v1.DispatchLookupResources2Request.resource_relation:type_name -> core.v1.RelationReference + 36, // 20: dispatch.v1.DispatchLookupResources2Request.subject_relation:type_name -> core.v1.RelationReference + 37, // 21: dispatch.v1.DispatchLookupResources2Request.terminal_subject:type_name -> core.v1.ObjectAndRelation + 40, // 22: dispatch.v1.DispatchLookupResources2Request.context:type_name -> google.protobuf.Struct 12, // 23: dispatch.v1.DispatchLookupResources2Request.optional_cursor:type_name -> dispatch.v1.Cursor 14, // 24: dispatch.v1.DispatchLookupResources2Response.resource:type_name -> dispatch.v1.PossibleResource 24, // 25: dispatch.v1.DispatchLookupResources2Response.metadata:type_name -> dispatch.v1.ResponseMeta 12, // 26: dispatch.v1.DispatchLookupResources2Response.after_response_cursor:type_name -> dispatch.v1.Cursor 23, // 27: dispatch.v1.DispatchLookupResources3Request.metadata:type_name -> dispatch.v1.ResolverMeta - 35, // 28: dispatch.v1.DispatchLookupResources3Request.resource_relation:type_name -> core.v1.RelationReference - 35, // 29: dispatch.v1.DispatchLookupResources3Request.subject_relation:type_name -> core.v1.RelationReference - 36, // 30: dispatch.v1.DispatchLookupResources3Request.terminal_subject:type_name -> core.v1.ObjectAndRelation - 39, // 31: dispatch.v1.DispatchLookupResources3Request.context:type_name -> google.protobuf.Struct + 36, // 28: dispatch.v1.DispatchLookupResources3Request.resource_relation:type_name -> core.v1.RelationReference + 36, // 29: dispatch.v1.DispatchLookupResources3Request.subject_relation:type_name -> core.v1.RelationReference + 37, // 30: dispatch.v1.DispatchLookupResources3Request.terminal_subject:type_name -> core.v1.ObjectAndRelation + 40, // 31: dispatch.v1.DispatchLookupResources3Request.context:type_name -> google.protobuf.Struct 18, // 32: dispatch.v1.DispatchLookupResources3Response.items:type_name -> dispatch.v1.LR3Item 23, // 33: dispatch.v1.DispatchLookupSubjectsRequest.metadata:type_name -> dispatch.v1.ResolverMeta - 35, // 34: dispatch.v1.DispatchLookupSubjectsRequest.resource_relation:type_name -> core.v1.RelationReference - 35, // 35: dispatch.v1.DispatchLookupSubjectsRequest.subject_relation:type_name -> core.v1.RelationReference - 37, // 36: dispatch.v1.FoundSubject.caveat_expression:type_name -> core.v1.CaveatExpression + 36, // 34: dispatch.v1.DispatchLookupSubjectsRequest.resource_relation:type_name -> core.v1.RelationReference + 36, // 35: dispatch.v1.DispatchLookupSubjectsRequest.subject_relation:type_name -> core.v1.RelationReference + 38, // 36: dispatch.v1.FoundSubject.caveat_expression:type_name -> core.v1.CaveatExpression 20, // 37: dispatch.v1.FoundSubject.excluded_subjects:type_name -> dispatch.v1.FoundSubject 20, // 38: dispatch.v1.FoundSubjects.found_subjects:type_name -> dispatch.v1.FoundSubject - 33, // 39: dispatch.v1.DispatchLookupSubjectsResponse.found_subjects_by_resource_id:type_name -> dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry + 34, // 39: dispatch.v1.DispatchLookupSubjectsResponse.found_subjects_by_resource_id:type_name -> dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry 24, // 40: dispatch.v1.DispatchLookupSubjectsResponse.metadata:type_name -> dispatch.v1.ResponseMeta 25, // 41: dispatch.v1.ResponseMeta.debug_info:type_name -> dispatch.v1.DebugInformation 26, // 42: dispatch.v1.DebugInformation.check:type_name -> dispatch.v1.CheckDebugTrace 6, // 43: dispatch.v1.CheckDebugTrace.request:type_name -> dispatch.v1.DispatchCheckRequest 5, // 44: dispatch.v1.CheckDebugTrace.resource_relation_type:type_name -> dispatch.v1.CheckDebugTrace.RelationType - 34, // 45: dispatch.v1.CheckDebugTrace.results:type_name -> dispatch.v1.CheckDebugTrace.ResultsEntry + 35, // 45: dispatch.v1.CheckDebugTrace.results:type_name -> dispatch.v1.CheckDebugTrace.ResultsEntry 26, // 46: dispatch.v1.CheckDebugTrace.sub_problems:type_name -> dispatch.v1.CheckDebugTrace - 40, // 47: dispatch.v1.CheckDebugTrace.duration:type_name -> google.protobuf.Duration - 39, // 48: dispatch.v1.PlanContext.caveat_context:type_name -> google.protobuf.Struct - 0, // 49: dispatch.v1.PlanContext.top_level_operation:type_name -> dispatch.v1.PlanOperation - 0, // 50: dispatch.v1.DispatchQueryPlanRequest.operation:type_name -> dispatch.v1.PlanOperation - 36, // 51: dispatch.v1.DispatchQueryPlanRequest.resource:type_name -> core.v1.ObjectAndRelation - 36, // 52: dispatch.v1.DispatchQueryPlanRequest.subject:type_name -> core.v1.ObjectAndRelation - 27, // 53: dispatch.v1.DispatchQueryPlanRequest.plan_context:type_name -> dispatch.v1.PlanContext - 36, // 54: dispatch.v1.DispatchQueryPlanRequest.many:type_name -> core.v1.ObjectAndRelation - 24, // 55: dispatch.v1.DispatchQueryPlanResponse.metadata:type_name -> dispatch.v1.ResponseMeta - 30, // 56: dispatch.v1.DispatchQueryPlanResponse.paths:type_name -> dispatch.v1.ResultPath - 37, // 57: dispatch.v1.ResultPath.caveat:type_name -> core.v1.CaveatExpression - 41, // 58: dispatch.v1.ResultPath.expiration:type_name -> google.protobuf.Timestamp - 42, // 59: dispatch.v1.ResultPath.integrity:type_name -> core.v1.RelationshipIntegrity - 39, // 60: dispatch.v1.ResultPath.metadata:type_name -> google.protobuf.Struct - 30, // 61: dispatch.v1.ResultPath.excluded_subjects:type_name -> dispatch.v1.ResultPath - 9, // 62: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry.value:type_name -> dispatch.v1.ResourceCheckResult - 21, // 63: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry.value:type_name -> dispatch.v1.FoundSubjects - 9, // 64: dispatch.v1.CheckDebugTrace.ResultsEntry.value:type_name -> dispatch.v1.ResourceCheckResult - 6, // 65: dispatch.v1.DispatchService.DispatchCheck:input_type -> dispatch.v1.DispatchCheckRequest - 10, // 66: dispatch.v1.DispatchService.DispatchExpand:input_type -> dispatch.v1.DispatchExpandRequest - 19, // 67: dispatch.v1.DispatchService.DispatchLookupSubjects:input_type -> dispatch.v1.DispatchLookupSubjectsRequest - 13, // 68: dispatch.v1.DispatchService.DispatchLookupResources2:input_type -> dispatch.v1.DispatchLookupResources2Request - 16, // 69: dispatch.v1.DispatchService.DispatchLookupResources3:input_type -> dispatch.v1.DispatchLookupResources3Request - 28, // 70: dispatch.v1.DispatchService.DispatchQueryPlan:input_type -> dispatch.v1.DispatchQueryPlanRequest - 8, // 71: dispatch.v1.DispatchService.DispatchCheck:output_type -> dispatch.v1.DispatchCheckResponse - 11, // 72: dispatch.v1.DispatchService.DispatchExpand:output_type -> dispatch.v1.DispatchExpandResponse - 22, // 73: dispatch.v1.DispatchService.DispatchLookupSubjects:output_type -> dispatch.v1.DispatchLookupSubjectsResponse - 15, // 74: dispatch.v1.DispatchService.DispatchLookupResources2:output_type -> dispatch.v1.DispatchLookupResources2Response - 17, // 75: dispatch.v1.DispatchService.DispatchLookupResources3:output_type -> dispatch.v1.DispatchLookupResources3Response - 29, // 76: dispatch.v1.DispatchService.DispatchQueryPlan:output_type -> dispatch.v1.DispatchQueryPlanResponse - 71, // [71:77] is the sub-list for method output_type - 65, // [65:71] is the sub-list for method input_type - 65, // [65:65] is the sub-list for extension type_name - 65, // [65:65] is the sub-list for extension extendee - 0, // [0:65] is the sub-list for field type_name + 41, // 47: dispatch.v1.CheckDebugTrace.duration:type_name -> google.protobuf.Duration + 42, // 48: dispatch.v1.CheckDebugTrace.start_time:type_name -> google.protobuf.Timestamp + 27, // 49: dispatch.v1.CheckDebugTrace.datastore_queries:type_name -> dispatch.v1.DatastoreQuery + 42, // 50: dispatch.v1.DatastoreQuery.start_time:type_name -> google.protobuf.Timestamp + 41, // 51: dispatch.v1.DatastoreQuery.duration:type_name -> google.protobuf.Duration + 40, // 52: dispatch.v1.PlanContext.caveat_context:type_name -> google.protobuf.Struct + 0, // 53: dispatch.v1.PlanContext.top_level_operation:type_name -> dispatch.v1.PlanOperation + 0, // 54: dispatch.v1.DispatchQueryPlanRequest.operation:type_name -> dispatch.v1.PlanOperation + 37, // 55: dispatch.v1.DispatchQueryPlanRequest.resource:type_name -> core.v1.ObjectAndRelation + 37, // 56: dispatch.v1.DispatchQueryPlanRequest.subject:type_name -> core.v1.ObjectAndRelation + 28, // 57: dispatch.v1.DispatchQueryPlanRequest.plan_context:type_name -> dispatch.v1.PlanContext + 37, // 58: dispatch.v1.DispatchQueryPlanRequest.many:type_name -> core.v1.ObjectAndRelation + 24, // 59: dispatch.v1.DispatchQueryPlanResponse.metadata:type_name -> dispatch.v1.ResponseMeta + 31, // 60: dispatch.v1.DispatchQueryPlanResponse.paths:type_name -> dispatch.v1.ResultPath + 38, // 61: dispatch.v1.ResultPath.caveat:type_name -> core.v1.CaveatExpression + 42, // 62: dispatch.v1.ResultPath.expiration:type_name -> google.protobuf.Timestamp + 43, // 63: dispatch.v1.ResultPath.integrity:type_name -> core.v1.RelationshipIntegrity + 40, // 64: dispatch.v1.ResultPath.metadata:type_name -> google.protobuf.Struct + 31, // 65: dispatch.v1.ResultPath.excluded_subjects:type_name -> dispatch.v1.ResultPath + 9, // 66: dispatch.v1.DispatchCheckResponse.ResultsByResourceIdEntry.value:type_name -> dispatch.v1.ResourceCheckResult + 21, // 67: dispatch.v1.DispatchLookupSubjectsResponse.FoundSubjectsByResourceIdEntry.value:type_name -> dispatch.v1.FoundSubjects + 9, // 68: dispatch.v1.CheckDebugTrace.ResultsEntry.value:type_name -> dispatch.v1.ResourceCheckResult + 6, // 69: dispatch.v1.DispatchService.DispatchCheck:input_type -> dispatch.v1.DispatchCheckRequest + 10, // 70: dispatch.v1.DispatchService.DispatchExpand:input_type -> dispatch.v1.DispatchExpandRequest + 19, // 71: dispatch.v1.DispatchService.DispatchLookupSubjects:input_type -> dispatch.v1.DispatchLookupSubjectsRequest + 13, // 72: dispatch.v1.DispatchService.DispatchLookupResources2:input_type -> dispatch.v1.DispatchLookupResources2Request + 16, // 73: dispatch.v1.DispatchService.DispatchLookupResources3:input_type -> dispatch.v1.DispatchLookupResources3Request + 29, // 74: dispatch.v1.DispatchService.DispatchQueryPlan:input_type -> dispatch.v1.DispatchQueryPlanRequest + 8, // 75: dispatch.v1.DispatchService.DispatchCheck:output_type -> dispatch.v1.DispatchCheckResponse + 11, // 76: dispatch.v1.DispatchService.DispatchExpand:output_type -> dispatch.v1.DispatchExpandResponse + 22, // 77: dispatch.v1.DispatchService.DispatchLookupSubjects:output_type -> dispatch.v1.DispatchLookupSubjectsResponse + 15, // 78: dispatch.v1.DispatchService.DispatchLookupResources2:output_type -> dispatch.v1.DispatchLookupResources2Response + 17, // 79: dispatch.v1.DispatchService.DispatchLookupResources3:output_type -> dispatch.v1.DispatchLookupResources3Response + 30, // 80: dispatch.v1.DispatchService.DispatchQueryPlan:output_type -> dispatch.v1.DispatchQueryPlanResponse + 75, // [75:81] is the sub-list for method output_type + 69, // [69:75] is the sub-list for method input_type + 69, // [69:69] is the sub-list for extension type_name + 69, // [69:69] is the sub-list for extension extendee + 0, // [0:69] is the sub-list for field type_name } func init() { file_dispatch_v1_dispatch_proto_init() } @@ -2524,7 +2627,7 @@ func file_dispatch_v1_dispatch_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_dispatch_v1_dispatch_proto_rawDesc), len(file_dispatch_v1_dispatch_proto_rawDesc)), NumEnums: 6, - NumMessages: 29, + NumMessages: 30, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go index dbf0524433..05f832dd48 100644 --- a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go +++ b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go @@ -617,6 +617,7 @@ func (m *CheckDebugTrace) CloneVT() *CheckDebugTrace { r.ResourceRelationType = m.ResourceRelationType r.IsCachedResult = m.IsCachedResult r.Duration = (*durationpb.Duration)((*durationpb1.Duration)(m.Duration).CloneVT()) + r.StartTime = (*timestamppb.Timestamp)((*timestamppb1.Timestamp)(m.StartTime).CloneVT()) r.TraceId = m.TraceId r.SourceId = m.SourceId if rhs := m.Results; rhs != nil { @@ -633,6 +634,13 @@ func (m *CheckDebugTrace) CloneVT() *CheckDebugTrace { } r.SubProblems = tmpContainer } + if rhs := m.DatastoreQueries; rhs != nil { + tmpContainer := make([]*DatastoreQuery, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.DatastoreQueries = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -644,6 +652,26 @@ func (m *CheckDebugTrace) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *DatastoreQuery) CloneVT() *DatastoreQuery { + if m == nil { + return (*DatastoreQuery)(nil) + } + r := new(DatastoreQuery) + r.QueryShape = m.QueryShape + r.StartTime = (*timestamppb.Timestamp)((*timestamppb1.Timestamp)(m.StartTime).CloneVT()) + r.Duration = (*durationpb.Duration)((*durationpb1.Duration)(m.Duration).CloneVT()) + r.RelationshipCount = m.RelationshipCount + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *DatastoreQuery) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *PlanContext) CloneVT() *PlanContext { if m == nil { return (*PlanContext)(nil) @@ -1679,6 +1707,26 @@ func (this *CheckDebugTrace) EqualVT(that *CheckDebugTrace) bool { if this.SourceId != that.SourceId { return false } + if !(*timestamppb1.Timestamp)(this.StartTime).EqualVT((*timestamppb1.Timestamp)(that.StartTime)) { + return false + } + if len(this.DatastoreQueries) != len(that.DatastoreQueries) { + return false + } + for i, vx := range this.DatastoreQueries { + vy := that.DatastoreQueries[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &DatastoreQuery{} + } + if q == nil { + q = &DatastoreQuery{} + } + if !p.EqualVT(q) { + return false + } + } + } return string(this.unknownFields) == string(that.unknownFields) } @@ -1689,6 +1737,34 @@ func (this *CheckDebugTrace) EqualMessageVT(thatMsg proto.Message) bool { } return this.EqualVT(that) } +func (this *DatastoreQuery) EqualVT(that *DatastoreQuery) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.QueryShape != that.QueryShape { + return false + } + if !(*timestamppb1.Timestamp)(this.StartTime).EqualVT((*timestamppb1.Timestamp)(that.StartTime)) { + return false + } + if !(*durationpb1.Duration)(this.Duration).EqualVT((*durationpb1.Duration)(that.Duration)) { + return false + } + if this.RelationshipCount != that.RelationshipCount { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DatastoreQuery) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DatastoreQuery) + if !ok { + return false + } + return this.EqualVT(that) +} func (this *PlanContext) EqualVT(that *PlanContext) bool { if this == that { return true @@ -3500,6 +3576,28 @@ func (m *CheckDebugTrace) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.DatastoreQueries) > 0 { + for iNdEx := len(m.DatastoreQueries) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.DatastoreQueries[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x52 + } + } + if m.StartTime != nil { + size, err := (*timestamppb1.Timestamp)(m.StartTime).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x4a + } if len(m.SourceId) > 0 { i -= len(m.SourceId) copy(dAtA[i:], m.SourceId) @@ -3586,6 +3684,71 @@ func (m *CheckDebugTrace) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *DatastoreQuery) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DatastoreQuery) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DatastoreQuery) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.RelationshipCount != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RelationshipCount)) + i-- + dAtA[i] = 0x20 + } + if m.Duration != nil { + size, err := (*durationpb1.Duration)(m.Duration).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + if m.StartTime != nil { + size, err := (*timestamppb1.Timestamp)(m.StartTime).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.QueryShape) > 0 { + i -= len(m.QueryShape) + copy(dAtA[i:], m.QueryShape) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.QueryShape))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *PlanContext) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -4717,6 +4880,41 @@ func (m *CheckDebugTrace) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + if m.StartTime != nil { + l = (*timestamppb1.Timestamp)(m.StartTime).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.DatastoreQueries) > 0 { + for _, e := range m.DatastoreQueries { + l = e.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *DatastoreQuery) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.QueryShape) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.StartTime != nil { + l = (*timestamppb1.Timestamp)(m.StartTime).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Duration != nil { + l = (*durationpb1.Duration)(m.Duration).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.RelationshipCount != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.RelationshipCount)) + } n += len(m.unknownFields) return n } @@ -8913,6 +9111,250 @@ func (m *CheckDebugTrace) UnmarshalVT(dAtA []byte) error { } m.SourceId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.StartTime == nil { + m.StartTime = ×tamppb.Timestamp{} + } + if err := (*timestamppb1.Timestamp)(m.StartTime).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DatastoreQueries", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DatastoreQueries = append(m.DatastoreQueries, &DatastoreQuery{}) + if err := m.DatastoreQueries[len(m.DatastoreQueries)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DatastoreQuery) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DatastoreQuery: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DatastoreQuery: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field QueryShape", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.QueryShape = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.StartTime == nil { + m.StartTime = ×tamppb.Timestamp{} + } + if err := (*timestamppb1.Timestamp)(m.StartTime).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Duration == nil { + m.Duration = &durationpb.Duration{} + } + if err := (*durationpb1.Duration)(m.Duration).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RelationshipCount", wireType) + } + m.RelationshipCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RelationshipCount |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/proto/internal/dispatch/v1/dispatch.proto b/proto/internal/dispatch/v1/dispatch.proto index 1d80a5e127..19337dcbbf 100644 --- a/proto/internal/dispatch/v1/dispatch.proto +++ b/proto/internal/dispatch/v1/dispatch.proto @@ -219,8 +219,20 @@ message CheckDebugTrace { bool is_cached_result = 4; repeated CheckDebugTrace sub_problems = 5; google.protobuf.Duration duration = 6; + // start_time is the wall-clock start of this Check; end = start_time + duration. + google.protobuf.Timestamp start_time = 9; string trace_id = 7; string source_id = 8; + // datastore_queries are the relationship queries this node issued itself. + repeated DatastoreQuery datastore_queries = 10; +} + +// DatastoreQuery records a single relationship query issued while resolving a Check node. +message DatastoreQuery { + string query_shape = 1; + google.protobuf.Timestamp start_time = 2; + google.protobuf.Duration duration = 3; + uint64 relationship_count = 4; } enum PlanOperation {