refactor: check using weighted graph#2816
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughAdds a new weighted-graph authorization check subsystem: request/response modeling, a pluggable Resolver with Default/Recursive/Weight2/bottom-up strategies, iterator utilities, a modelgraph package with cached resolver, server integration behind experimental flags, and extensive unit tests and mocks. Changes
Sequence DiagramsequenceDiagram
actor Client
participant Server
participant CheckQueryV2
participant Resolver
participant ModelGraph
participant Strategy
participant BottomUp
participant Datastore
Client->>Server: CheckRequest
Server->>CheckQueryV2: Execute(ctx, req)
CheckQueryV2->>ModelGraph: Ensure/Resolve model graph
ModelGraph-->>CheckQueryV2: AuthorizationModelGraph
CheckQueryV2->>Resolver: New(Config) / ResolveCheck(ctx, Request)
Resolver->>ModelGraph: Validate tuple types & build edges
Resolver->>Strategy: Select & Execute strategy (Default/Recursive/Weight2)
alt Default / Weight2 uses BottomUp
Strategy->>BottomUp: resolve rewrites / unions / operators
BottomUp->>Datastore: Read tuples / ReadStartingWithUser
Datastore-->>BottomUp: TupleKeys stream
BottomUp-->>Resolver: Streamed ResponseMsgs (allowed/error)
Resolver-->>Strategy: Aggregated Response
else Recursive strategy
Strategy->>Resolver: ResolveCheck per discovered ID (BFS)
Resolver->>Datastore: Read tuples per ID
Datastore-->>Resolver: Tuple streams
Resolver-->>Strategy: Responses aggregated with visited tracking
end
Strategy-->>Resolver: Final Response
Resolver-->>CheckQueryV2: Response
CheckQueryV2-->>Server: CheckResponse
Server-->>Client: CheckResponse
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
This comment has been minimized.
This comment has been minimized.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2816 +/- ##
==========================================
- Coverage 90.27% 90.00% -0.27%
==========================================
Files 169 186 +17
Lines 17665 19776 +2111
==========================================
+ Hits 15946 17798 +1852
- Misses 1134 1318 +184
- Partials 585 660 +75 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 14
♻️ Duplicate comments (2)
internal/iterator/channel.go (1)
33-41: Error handling loop continues on persistent errors.When the iterator returns a non-terminal error (not
IterIsDoneOrCancelled), the error is sent through the channel but the loop continues. If the underlying iterator consistently fails, this could flood the channel with error messages.internal/check/check.go (1)
800-822: Consider deduplicating contextual and database tuples.Contextual tuples are prepended to database tuples without deduplication. If the same tuple exists in both sources, it will be processed twice, which could lead to redundant condition evaluations and wasted computation.
The
BuildUniqueTupleKeyFilteronly deduplicates based on visited usersets during graph traversal, not between the two tuple sources.
🧹 Nitpick comments (29)
.golangci.yaml (1)
31-33: Verify necessity of disabling ifElseChain linter check.Disabling the
ifElseChaincheck indicates the new check engine uses complex if-else structures. While sometimes necessary, this often signals code that could benefit from refactoring (e.g., usingswitchstatements, polymorphism, or strategy patterns) for better readability and maintainability. Ensure this is intentional and that alternative patterns have been considered.internal/concurrency/concurrency.go (1)
3-13: Consider documentingErrShortCircuitfor future callersThe sentinel error itself is fine, but since it’s exported it would benefit from a brief Go-style comment describing when callers should return or check for this error (e.g., to short‑circuit concurrent work).
assets/tests/consolidated_1_1_tests.yaml (1)
2758-2812: Invalid test is correctly disabled; consider relocating itCommenting out the
exclusion_between_userset_and_typetest with an explanation is safe and keeps the suite aligned with current semantics. To avoid long‑term drift, consider moving this scenario into a dedicated “invalid model / unsupported patterns” test or documentation section instead of leaving a large commented block inline.pkg/storage/tuple_mappers.go (1)
72-83: Exported mapping helpers preserve behavior; add brief docs
MapUsersetcorrectly mirrors the previous userset mapping logic (including the guard on missing relations), andMapTTUsimply returns the user string, so existing mapper behavior is preserved. Since these helpers are now exported, consider:
- Adding short Go‑style comments explaining what each expects (e.g., that
MapUsersetis intended for tuples whoseuseris a userset and may return an error otherwise, and thattmust be non‑nil).- Optionally noting that
MapTTUcurrently never returns an error but uses the(string, error)shape for consistency and potential future validation.Also applies to: 114-120
internal/iterator/message.go (1)
5-8: Clean message types for iterator results.The
Msgstruct provides a clear way to transmit iterator results and errors through channels. The note at line 6 acknowledges thatItercould be made generic if needed in the future.Consider making
Msggeneric now to avoid a breaking change later:-type Msg struct { - Iter storage.Iterator[string] // NOTE: This could be made completely generic if needed +type Msg[T any] struct { + Iter storage.Iterator[T] Err error }However, this can be deferred if string iterators are the only use case for now.
internal/iterator/skip.go (1)
9-33: Logic is correct, but consider performance for large skips.The
SkipTofunction correctly advances the iterator to the target value using lexicographic comparison and properly handles context cancellation and iterator completion.For iterators with many elements to skip, this linear scan could be inefficient. If the underlying iterator supports seeking or batch operations, consider optimizing for large skip distances. However, for typical use cases with sorted tuples, this implementation should be adequate.
tests/check/check_test.go (1)
76-80: Track: Commented-out test code needs follow-up.The TODO comment indicates tests need deprecation before the weighted graph check can be enabled for the full test suite. Consider creating a tracking issue to ensure this doesn't get forgotten.
Would you like me to open an issue to track enabling the weighted graph check tests after the necessary deprecations?
internal/check/default_test.go (1)
26-27: Remove unused gomock controller at test function level.The
ctrlcreated at line 26-27 is never used since each subtest creates its own controller (line 204-205). This is harmless but creates unnecessary allocation.func TestDefaultUserset(t *testing.T) { t.Cleanup(func() { goleak.VerifyNone(t) }) - ctrl := gomock.NewController(t) - defer ctrl.Finish() storeID := ulid.Make().String()pkg/server/server.go (1)
895-897: Add TTL configuration option for authzModelGraphResolver to align with cache settings pattern.The hardcoded 168-hour TTL should be configurable. The codebase already has established patterns for cache TTL configuration (e.g.,
WithCheckQueryCacheTTL,WithCheckIteratorCacheTTL). TheauthzModelGraphResolvershould use a similar configurable option rather than hardcoding the value, especially since the TODO acknowledges this need.Regarding lifecycle management:
AuthorizationModelGraphResolverhas noClose()orStop()methods and does not own the cache (it's passed in vias.sharedDatastoreResources.CheckCache), so no cleanup inServer.Close()is required.internal/iterator/merge_test.go (1)
15-23: Consider adding goroutine leak detection for consistency.The
concat_test.gofile usesgoleak.VerifyNone(t)for detecting goroutine leaks. For consistency across the iterator test suite, consider adding the same here.func TestMergedIterator(t *testing.T) { + t.Cleanup(func() { + goleak.VerifyNone(t) + }) + compareFn := func(a, b string) int {You'll also need to add the import:
import "go.uber.org/goleak"internal/iterator/concat.go (1)
82-85: Consider using a sentinel error for unsupported Head operation.Using a plain
fmt.Errorfmakes it harder for callers to programmatically check for this specific condition. A sentinel error would allowerrors.Is()checks.+var ErrHeadNotSupported = errors.New("head() not supported on concat iterator") + func (c *concatIterator[T]) Head(_ context.Context) (T, error) { var zero T - return zero, fmt.Errorf("head() not supported on concat iterator") + return zero, ErrHeadNotSupported }internal/check/filters.go (1)
17-23: Potential nil map access and O(n) lookup on every tuple.Two concerns:
slices.Contains(conditions, ...)is O(n) per tuple. Ifconditionsis large and many tuples are processed, consider using amap[string]struct{}for O(1) lookups.model.GetConditions()[t.GetCondition().GetName()]may returnnilif the condition name exists inconditionsbut not in the model's condition map, which could cause issues inEvaluateTupleCondition.Consider converting
conditionsto a set for better performance:-func evaluateCondition(ctx context.Context, model *modelgraph.AuthorizationModelGraph, conditions []string, t *openfgav1.TupleKey, reqCtx *structpb.Struct) (bool, error) { - if !slices.Contains(conditions, t.GetCondition().GetName()) { +func evaluateCondition(ctx context.Context, model *modelgraph.AuthorizationModelGraph, conditionsSet map[string]struct{}, t *openfgav1.TupleKey, reqCtx *structpb.Struct) (bool, error) { + if _, ok := conditionsSet[t.GetCondition().GetName()]; !ok { return false, nil }The set conversion can happen once in
BuildConditionTupleKeyFilterrather than on every filter invocation.internal/iterator/filter_test.go (1)
239-253: SharedseenTuplesstate may cause flaky tests.The
seenTuplessync.Map anduniqueFilterare defined at the test function level but used byboth_filters_appliedsub-test. Since this map accumulates state, running this test multiple times or in parallel could cause unexpected behavior. Consider moving the map initialization inside the sub-test.func TestFilter_MultipleFilters(t *testing.T) { conditionFilter := func(_ OperationType, tupleKey *openfgav1.TupleKey) (bool, error) { return tupleKey.GetCondition().GetName() == "condition1", nil } - seenTuples := &sync.Map{} - //nolint:unparam - uniqueFilter := func(_ OperationType, tupleKey *openfgav1.TupleKey) (bool, error) { - key := tuple.TupleKeyToString(tupleKey) - if _, exists := seenTuples.LoadOrStore(key, struct{}{}); exists { - return false, nil - } - return true, nil - } - t.Run("both_filters_applied", func(t *testing.T) { + seenTuples := &sync.Map{} + uniqueFilter := func(_ OperationType, tupleKey *openfgav1.TupleKey) (bool, error) { + key := tuple.TupleKeyToString(tupleKey) + if _, exists := seenTuples.LoadOrStore(key, struct{}{}); exists { + return false, nil + } + return true, nil + } + tuples := []*openfgav1.TupleKey{internal/check/weight2_test.go (2)
411-412: Misleading comment: MaxTimes(3) does not mean "any number of calls".The comment states "Allow any number of calls" but
MaxTimes(3)limits expectations to at most 3 calls. Either update the comment to be accurate or useAnyTimes()if truly unlimited calls are intended.mockDatastore.EXPECT().ReadStartingWithUser(gomock.Any(), storeID, gomock.Any(), gomock.Any()). - MaxTimes(3). // Allow any number of calls + MaxTimes(3). // Allow up to 3 calls (members, public_restricted, restricted) DoAndReturn(func(ctx context.Context, sID string, filter storage.ReadStartingWithUserFilter, opts storage.ReadStartingWithUserOptions) (storage.TupleIterator, error) {
867-869: Same misleading comment pattern.mockDatastore.EXPECT().ReadStartingWithUser(gomock.Any(), storeID, gomock.Any(), gomock.Any()). - MaxTimes(3). // Allow any number of calls + MaxTimes(3). // Allow up to 3 calls (members, public_restricted, restricted) DoAndReturn(func(ctx context.Context, sID string, filter storage.ReadStartingWithUserFilter, opts storage.ReadStartingWithUserOptions) (storage.TupleIterator, error) {internal/check/bottom_up_test.go (2)
1721-1722: Misleading comment: MaxTimes(3) limits to at most 3 calls.mockDatastore.EXPECT().ReadStartingWithUser(gomock.Any(), storeID, gomock.Any(), gomock.Any()). - MaxTimes(3). // Allow any number of calls + MaxTimes(3). // Allow up to 3 calls for viewer, editor, commenter DoAndReturn(func(ctx context.Context, sID string, filter storage.ReadStartingWithUserFilter, opts storage.ReadStartingWithUserOptions) (storage.TupleIterator, error) {
1838-1839: Same misleading comment pattern.mockDatastore.EXPECT().ReadStartingWithUser(gomock.Any(), storeID, gomock.Any(), gomock.Any()). - MaxTimes(3). // Allow any number of calls + MaxTimes(3). // Allow up to 3 calls for viewer, editor, commenter DoAndReturn(func(ctx context.Context, sID string, filter storage.ReadStartingWithUserFilter, opts storage.ReadStartingWithUserOptions) (storage.TupleIterator, error) {pkg/server/check.go (1)
187-189: Consider bounding shadow check concurrency.The shadow v2 check launches an unbounded goroutine for each request when the feature flag is enabled. Under high load, this could consume excessive resources. Consider using a semaphore or worker pool to limit concurrent shadow checks.
internal/iterator/merge.go (1)
30-52: Minor: initialize() sets flag before completion.The
initializedflag is set to true (line 34) before fetching values from either iterator. If an error occurs during initialization, subsequent calls toinitialize()will return nil, potentially leaving the iterator in a partially initialized state.Consider setting
initialized = trueafter successful initialization:func (m *MergedIterator[T]) initialize(ctx context.Context) error { if m.initialized { return nil } - m.initialized = true // Fetch first value from iter1 current, err := m.iter1.Next(ctx) if err != nil && !errors.Is(err, storage.ErrIteratorDone) { return err } m.hasNext1 = !errors.Is(err, storage.ErrIteratorDone) m.current1 = current // Fetch first value from iter2 current2, err2 := m.iter2.Next(ctx) if err2 != nil && !errors.Is(err2, storage.ErrIteratorDone) { return err2 } m.hasNext2 = !errors.Is(err2, storage.ErrIteratorDone) m.current2 = current2 + + m.initialized = true return nil }internal/iterator/channel.go (1)
133-146: Stop() doesn't await Drain() completion.The
Stop()method callsDrain(c.source)(line 145) but discards the returned*sync.WaitGroup. This meansStop()returns before the channel is fully drained and nested iterators are stopped. Consider waiting for drain completion or documenting this as intentional fire-and-forget behavior.func (c *channelIterator) Stop() { if c.stopped { return } c.stopped = true if c.current != nil { c.current.Stop() c.current = nil } // Drain remaining messages and stop their iterators - Drain(c.source) + if wg := Drain(c.source); wg != nil { + wg.Wait() + } }internal/check/default.go (1)
96-130: Consider documenting the buffer size choice.The
responsesChanbuffer size of 100 is hardcoded. While the comment mentions it "needs to be buffered to prevent out of order closed events," a brief explanation of why 100 specifically (or making it proportional toconcurrencyLimit) would help future maintainers.- responsesChan := make(chan ResponseMsg, 100) // needs to be buffered to prevent out of order closed events + // Buffer size should accommodate in-flight responses from concurrent workers to prevent + // blocking when the consumer is processing a successful result. Using concurrencyLimit + // as a baseline ensures workers won't block waiting to send responses. + responsesChan := make(chan ResponseMsg, s.concurrencyLimit)internal/check/strategies.go (1)
10-55: Inconsistent export naming for plan configurations.
weight2Planis unexported (lowercase) whileDefaultPlan,DefaultRecursivePlan, andRecursivePlanare exported. Ifweight2Planis intentionally package-private, this is fine, but it creates an inconsistency in the API surface.If this should be exported for consistency:
-var weight2Plan = &planner.PlanConfig{ +var Weight2Plan = &planner.PlanConfig{internal/check/weight2.go (2)
15-17: Unused constantsBaseIndexandDifferenceIndex.These constants are defined but not used anywhere in this file. If they're intended for use elsewhere, consider moving them to a shared location or removing them if unused.
69-74: Redundant context cancellation.The caller (
Userset/TTU) doesn't create a cancellable context before callingexecute. Whiledefer cancel()is defensive, consider whether the caller should own context cancellation for consistency, or document whyexecutecreates its own cancellation scope.pkg/server/commands/check.go (1)
85-95: Consider validating required dependencies.
NewCheckQueryonly sets a default logger. IfExecuteis called without settingmodel,datastore, orplanner, it may cause nil pointer dereferences or unexpected behavior downstream. Consider either:
- Requiring critical dependencies as constructor parameters
- Adding validation in
Executebefore proceedingfunc NewCheckQuery(opts ...CheckQueryV2Option) *CheckQueryV2 { q := &CheckQueryV2{ logger: logger.NewNoopLogger(), } for _, opt := range opts { opt(q) } return q } + +func (q *CheckQueryV2) validate() error { + if q.model == nil { + return errors.New("model is required") + } + if q.datastore == nil { + return errors.New("datastore is required") + } + return nil +}internal/check/recursive.go (1)
189-192: Goroutine leak if context is cancelled before pool.Wait completes.The goroutine that closes
responsesChanruns independently. If the parent context is cancelled andrecursiveMatchreturns early (line 197), this goroutine continues to run untilpool.Wait()completes, which could be delayed if goroutines are blocked.Consider using a
selectwith context cancellation in the wait goroutine:go func() { - _ = pool.Wait() - close(responsesChan) + done := make(chan struct{}) + go func() { + _ = pool.Wait() + close(done) + }() + <-done + close(responsesChan) }()internal/modelgraph/model.go (2)
14-16: Consider adding sentinel error wrapping for better diagnostics.
ErrGraphErroris generic and used in multiple places. Consider wrapping with additional context where it's returned, or defining more specific errors.
54-71: Consider more specific error for "edge not found for user type".When no edge matches the
userType, the method returns a genericErrGraphError. A more specific error would help callers distinguish between "node not found", "no edges from node", and "no edge matching user type".tests/check/check.go (1)
126-129: Skipping tests on model graph failure may hide real issues.Using
t.Skipfwhenmodelgraph.Newfails means tests silently pass if there's a model graph construction bug. Consider usingt.Fatalfto fail the test explicitly, or log a warning if intentionally skipping unsupported models._, err := modelgraph.New(model) if err != nil { - t.Skipf("modelgraph.New failed: %v", err) + t.Fatalf("modelgraph.New failed: %v", err) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (51)
.golangci.yaml(1 hunks)assets/tests/consolidated_1_1_tests.yaml(1 hunks)go.mod(1 hunks)internal/check/bottom_up.go(1 hunks)internal/check/bottom_up_test.go(1 hunks)internal/check/check.go(1 hunks)internal/check/default.go(1 hunks)internal/check/default_test.go(1 hunks)internal/check/filters.go(1 hunks)internal/check/interface.go(1 hunks)internal/check/mock_resolver.go(1 hunks)internal/check/recursive.go(1 hunks)internal/check/recursive_test.go(1 hunks)internal/check/request.go(1 hunks)internal/check/request_test.go(1 hunks)internal/check/response.go(1 hunks)internal/check/strategies.go(1 hunks)internal/check/weight2.go(1 hunks)internal/check/weight2_test.go(1 hunks)internal/concurrency/concurrency.go(1 hunks)internal/graph/resolve_check_request.go(6 hunks)internal/graph/weight_two_resolver_test.go(13 hunks)internal/iterator/channel.go(1 hunks)internal/iterator/channel_test.go(1 hunks)internal/iterator/concat.go(1 hunks)internal/iterator/concat_test.go(1 hunks)internal/iterator/fan_in.go(0 hunks)internal/iterator/filter.go(1 hunks)internal/iterator/filter_test.go(1 hunks)internal/iterator/merge.go(1 hunks)internal/iterator/merge_test.go(1 hunks)internal/iterator/message.go(1 hunks)internal/iterator/skip.go(1 hunks)internal/iterator/skip_test.go(1 hunks)internal/iterator/stream.go(2 hunks)internal/iterator/stream_test.go(0 hunks)internal/mocks/mock_planner.go(1 hunks)internal/modelgraph/model.go(1 hunks)internal/modelgraph/model_test.go(1 hunks)internal/modelgraph/resolver.go(1 hunks)internal/modelgraph/resolver_test.go(1 hunks)pkg/server/check.go(3 hunks)pkg/server/commands/check.go(1 hunks)pkg/server/commands/errors.go(3 hunks)pkg/server/config/config.go(1 hunks)pkg/server/server.go(3 hunks)pkg/storage/cache.go(1 hunks)pkg/storage/tuple_mappers.go(3 hunks)tests/check/check.go(9 hunks)tests/check/check_test.go(4 hunks)tests/check/check_userset.go(2 hunks)
💤 Files with no reviewable changes (2)
- internal/iterator/stream_test.go
- internal/iterator/fan_in.go
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-07-02T18:00:27.063Z
Learnt from: senojj
Repo: openfga/openfga PR: 2538
File: pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go:710-712
Timestamp: 2025-07-02T18:00:27.063Z
Learning: In the OpenFGA shared iterator implementation (pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go), multiple state loads within the same iteration cycle are intentional to observe state changes made by other goroutines. The design prioritizes real-time state visibility over consistency within a single iteration, which is appropriate for shared iterator architecture where multiple goroutines update shared state.
Applied to files:
internal/iterator/merge_test.gointernal/graph/weight_two_resolver_test.gointernal/iterator/message.gointernal/iterator/filter_test.gointernal/check/bottom_up.gointernal/iterator/channel_test.gointernal/iterator/merge.gointernal/iterator/concat.gointernal/iterator/channel.gointernal/iterator/filter.gointernal/iterator/concat_test.gointernal/check/bottom_up_test.gointernal/iterator/skip.gointernal/iterator/stream.go
📚 Learning: 2025-07-29T19:18:31.430Z
Learnt from: senojj
Repo: openfga/openfga PR: 2590
File: pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go:298-303
Timestamp: 2025-07-29T19:18:31.430Z
Learning: In the OpenFGA shared iterator implementation (pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go), the cleanup functions use CompareAndDelete to atomically check and remove items from the internal storage maps. The counters (sf.internalStorage.ctr and sharedIteratorCount) are only decremented when CompareAndDelete returns true, ensuring that metrics are decremented exactly once per item removal. If CompareAndDelete returns false, it means another cleanup call already removed the item and decremented the counters, preventing double-decrementing and metric drift.
Applied to files:
internal/iterator/merge_test.gointernal/check/bottom_up.gointernal/iterator/merge.gointernal/iterator/channel.gointernal/iterator/filter.go
📚 Learning: 2025-07-02T18:10:58.517Z
Learnt from: senojj
Repo: openfga/openfga PR: 2538
File: pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go:722-722
Timestamp: 2025-07-02T18:10:58.517Z
Learning: In the OpenFGA shared iterator implementation (pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go), the background fetching in fetchAndWait method intentionally uses context.Background() instead of propagating the request context. This design allows the shared background fetching to continue serving all iterator clones even when individual request contexts are cancelled, which is appropriate for the shared iterator architecture.
Applied to files:
internal/iterator/merge_test.gointernal/check/bottom_up.gointernal/iterator/merge.gointernal/iterator/concat.gointernal/iterator/channel.gointernal/check/recursive.gointernal/iterator/concat_test.gointernal/iterator/skip.go
📚 Learning: 2025-10-16T19:02:25.959Z
Learnt from: senojj
Repo: openfga/openfga PR: 2746
File: pkg/server/commands/reverseexpand/pipeline.go:428-510
Timestamp: 2025-10-16T19:02:25.959Z
Learning: In pkg/server/commands/reverseexpand/pipeline.go, the pipeline uses an indirect context cancellation pattern: when Pipeline::Build's returned iter.Seq is terminated early, the pipeline context is canceled, causing resolvers to stop consuming from sequences, which triggers deferred cleanup (cancel + Stop) in the query function. This means handleDirectEdge and handleTTUEdge can use context.Background() because cancellation propagates through the iteration protocol rather than direct context threading.
Applied to files:
internal/check/bottom_up.gointernal/check/recursive.go
📚 Learning: 2025-06-20T18:39:47.718Z
Learnt from: adriantam
Repo: openfga/openfga PR: 2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
Applied to files:
internal/check/bottom_up.gointernal/modelgraph/model_test.gointernal/check/weight2.gointernal/check/check.gointernal/modelgraph/model.go
📚 Learning: 2025-06-20T18:39:39.926Z
Learnt from: adriantam
Repo: openfga/openfga PR: 2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
Applied to files:
internal/check/bottom_up.gointernal/check/check.gointernal/modelgraph/model.go
📚 Learning: 2025-10-16T21:29:53.682Z
Learnt from: senojj
Repo: openfga/openfga PR: 2746
File: pkg/server/commands/reverseexpand/pipeline.go:463-505
Timestamp: 2025-10-16T21:29:53.682Z
Learning: In TTU (tuple-to-userset) edges within OpenFGA authorization models, the target of a tupleset edge cannot be a userset (i.e., cannot be of the form `type#relation`). The target must be just a type. Therefore, when building userFilter entries for TTU edge queries in `handleTTUEdge`, the `Relation` field is correctly left empty.
Applied to files:
internal/check/default_test.goassets/tests/consolidated_1_1_tests.yamlinternal/check/recursive_test.gointernal/check/check.gopkg/server/commands/check.gointernal/check/weight2_test.go
📚 Learning: 2025-07-02T18:10:35.417Z
Learnt from: senojj
Repo: openfga/openfga PR: 2538
File: pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go:725-732
Timestamp: 2025-07-02T18:10:35.417Z
Learning: In the OpenFGA shared iterator implementation (pkg/storage/storagewrappers/sharediterator/shared_iterator_datastore.go), the `s.fetching.Swap(true)` atomic operation acts as a guard to ensure only one goroutine can enter the fetch routine and update the shared state at any time. This prevents race conditions in state updates without requiring explicit locking, as only the goroutine that sees the previous fetching value as false gets to proceed with the fetch operation.
Applied to files:
internal/iterator/filter.go
🧬 Code graph analysis (27)
internal/iterator/merge_test.go (3)
pkg/storage/tuple_iterators.go (2)
NewStaticIterator(236-238)ErrIteratorDone(13-13)internal/iterator/merge.go (1)
Merge(22-28)internal/mocks/mock_iterator.go (1)
NewMockIterator(32-36)
pkg/server/server.go (1)
internal/modelgraph/resolver.go (2)
AuthorizationModelGraphResolver(22-26)NewResolver(28-34)
internal/graph/weight_two_resolver_test.go (3)
internal/iterator/message.go (1)
Msg(5-8)internal/iterator/stream.go (1)
NewStream(20-25)internal/mocks/mock_iterator.go (1)
NewMockIterator(32-36)
internal/iterator/message.go (1)
pkg/storage/tuple_iterators.go (1)
Iterator(17-30)
internal/iterator/filter_test.go (3)
internal/iterator/filter.go (1)
NewFilteredIterator(123-133)pkg/tuple/tuple.go (3)
NewTupleKeyWithCondition(172-182)TupleKeyToString(351-357)NewTupleKey(164-170)pkg/storage/tuple_iterators.go (2)
NewStaticTupleKeyIterator(136-143)ErrIteratorDone(13-13)
internal/iterator/channel_test.go (4)
internal/iterator/message.go (1)
Msg(5-8)internal/mocks/mock_iterator.go (1)
NewMockIterator(32-36)internal/iterator/channel.go (2)
Drain(11-26)ToChannel(28-45)pkg/storage/tuple_iterators.go (3)
Iterator(17-30)ErrIteratorDone(13-13)IterIsDoneOrCancelled(544-546)
internal/iterator/merge.go (1)
pkg/storage/tuple_iterators.go (2)
Iterator(17-30)ErrIteratorDone(13-13)
internal/iterator/concat.go (1)
pkg/storage/tuple_iterators.go (2)
Iterator(17-30)ErrIteratorDone(13-13)
internal/iterator/skip_test.go (3)
pkg/storage/tuple_iterators.go (2)
NewStaticIterator(236-238)ErrIteratorDone(13-13)internal/iterator/skip.go (1)
SkipTo(9-33)internal/mocks/mock_iterator.go (1)
NewMockIterator(32-36)
internal/check/default_test.go (7)
pkg/testutils/testutils.go (1)
MustTransformDSLToProtoWithID(163-168)internal/check/mock_resolver.go (2)
MockCheckResolver(22-26)NewMockCheckResolver(34-38)internal/check/response.go (1)
Response(7-9)pkg/tuple/tuple.go (2)
NewTupleKey(164-170)GetRelation(325-328)internal/check/request.go (3)
Request(24-42)NewRequest(105-179)RequestParams(44-51)pkg/storage/tuple_iterators.go (1)
NewStaticTupleKeyIterator(136-143)internal/check/default.go (1)
NewDefault(32-38)
pkg/server/check.go (6)
pkg/server/config/config.go (2)
ExperimentalWeightedGraphCheck(107-107)ExperimentalShadowWeightedGraphCheck(106-106)pkg/server/server.go (1)
Server(161-244)internal/modelgraph/model.go (1)
ErrInvalidModel(16-16)internal/cachecontroller/cache_controller.go (1)
CacheController(58-70)pkg/server/commands/check.go (2)
NewCheckQuery(85-95)WithCheckQueryV2Datastore(37-41)pkg/server/commands/errors.go (1)
CheckCommandErrorToServerError(54-97)
internal/iterator/channel.go (3)
internal/iterator/message.go (2)
Msg(5-8)ValueMsg(10-13)pkg/storage/tuple_iterators.go (3)
Iterator(17-30)IterIsDoneOrCancelled(544-546)ErrIteratorDone(13-13)internal/concurrency/concurrency.go (1)
TrySendThroughChannel(26-33)
internal/mocks/mock_planner.go (2)
internal/planner/interface.go (1)
Selector(6-9)internal/planner/config.go (1)
PlanConfig(5-42)
internal/modelgraph/model_test.go (2)
pkg/testutils/testutils.go (1)
MustTransformDSLToProtoWithID(163-168)internal/modelgraph/model.go (1)
ErrGraphError(14-14)
internal/graph/resolve_check_request.go (1)
pkg/tuple/tuple.go (4)
GetType(318-321)IsObjectRelation(331-333)GetRelation(325-328)ToObjectRelationString(337-339)
tests/check/check_userset.go (1)
pkg/tuple/tuple.go (2)
Tuple(14-14)User(103-103)
internal/check/check.go (9)
pkg/storage/cache.go (1)
InMemoryCache(58-67)internal/planner/interface.go (2)
Manager(12-15)Selector(6-9)internal/check/default.go (1)
NewDefault(32-38)internal/check/weight2.go (1)
NewWeight2(25-31)internal/check/request.go (1)
Request(24-42)internal/check/response.go (3)
Response(7-9)ResponseCacheEntry(21-24)ResponseMsg(15-19)internal/graph/graph.go (1)
DirectEdge(49-49)internal/iterator/concat.go (1)
Concat(16-21)internal/iterator/filter.go (1)
FilterFunc(22-22)
internal/check/request.go (3)
internal/check/check.go (3)
New(63-86)ErrValidation(33-33)CacheKeyPrefix(29-29)pkg/tuple/tuple.go (9)
GetType(318-321)ToObjectRelationString(337-339)GetRelation(325-328)IsObjectRelation(331-333)IsTypedWildcard(396-399)IsValidUser(381-387)From(38-40)SplitObject(280-289)SplitObjectRelation(305-314)pkg/storage/cache.go (2)
WriteInvariantCheckCacheKey(411-437)CheckCacheKeyParams(381-387)
pkg/server/commands/check.go (3)
internal/check/request.go (2)
NewRequest(105-179)RequestParams(44-51)pkg/tuple/tuple.go (1)
ConvertCheckRequestTupleKeyToTupleKey(115-121)internal/check/check.go (2)
New(63-86)Config(37-48)
internal/check/weight2_test.go (8)
internal/mocks/mock_storage.go (1)
NewMockOpenFGADatastore(721-725)pkg/storage/storage.go (2)
ReadStartingWithUserFilter(289-303)ReadStartingWithUserOptions(133-136)pkg/storage/tuple_iterators.go (3)
NewStaticTupleIterator(126-133)NewStaticTupleKeyIterator(136-143)TupleIterator(32-32)pkg/tuple/tuple.go (3)
Tuple(14-14)NewTupleKey(164-170)User(103-103)pkg/testutils/testutils.go (1)
MustTransformDSLToProtoWithID(163-168)internal/modelgraph/model.go (1)
New(25-44)internal/check/weight2.go (1)
NewWeight2(25-31)internal/check/request.go (2)
NewRequest(105-179)RequestParams(44-51)
internal/iterator/concat_test.go (3)
pkg/storage/tuple_iterators.go (3)
Iterator(17-30)ErrIteratorDone(13-13)IterIsDoneOrCancelled(544-546)internal/mocks/mock_iterator.go (1)
NewMockIterator(32-36)internal/iterator/concat.go (1)
Concat(16-21)
internal/check/default.go (8)
internal/check/request.go (1)
Request(24-42)pkg/storage/tuple_iterators.go (1)
TupleKeyIterator(34-34)internal/modelgraph/model.go (1)
ErrGraphError(14-14)internal/check/interface.go (1)
CheckResolver(12-15)internal/check/response.go (2)
Response(7-9)ResponseMsg(15-19)internal/concurrency/concurrency.go (1)
TrySendThroughChannel(26-33)pkg/tuple/tuple.go (4)
SplitObjectRelation(305-314)NewTupleKey(164-170)ToObjectRelationString(337-339)GetRelation(325-328)internal/check/check.go (1)
ErrPanicRequest(34-34)
internal/check/bottom_up_test.go (10)
pkg/storage/storage.go (3)
ReadStartingWithUserFilter(289-303)ReadStartingWithUserOptions(133-136)ConsistencyOptions(109-111)pkg/storage/tuple_iterators.go (3)
NewStaticTupleIterator(126-133)Iterator(17-30)TupleIterator(32-32)pkg/testutils/testutils.go (1)
MustTransformDSLToProtoWithID(163-168)internal/check/request.go (2)
NewRequest(105-179)RequestParams(44-51)pkg/tuple/tuple.go (2)
NewTupleKey(164-170)Tuple(14-14)internal/iterator/message.go (1)
Msg(5-8)internal/mocks/mock_iterator.go (1)
NewMockIterator(32-36)internal/iterator/channel.go (1)
FromChannel(148-152)internal/concurrency/concurrency.go (1)
NewPool(16-22)internal/iterator/stream.go (1)
NewStream(20-25)
internal/check/filters.go (3)
internal/modelgraph/model.go (1)
AuthorizationModelGraph(18-23)internal/condition/eval/eval.go (1)
EvaluateTupleCondition(27-86)internal/iterator/filter.go (3)
FilterFunc(22-22)OperationType(12-12)OperationHead(16-16)
internal/iterator/skip.go (1)
pkg/storage/tuple_iterators.go (2)
Iterator(17-30)IterIsDoneOrCancelled(544-546)
internal/iterator/stream.go (2)
internal/iterator/channel.go (1)
Drain(11-26)pkg/storage/tuple_iterators.go (1)
IterIsDoneOrCancelled(544-546)
internal/check/mock_resolver.go (2)
internal/check/request.go (1)
Request(24-42)internal/check/response.go (1)
Response(7-9)
🪛 GitHub Actions: Pull Request
internal/check/check.go
[error] 440-440: Step: golangci-lint run -v -c .golangci.yaml. Error: stdversion: sync.Go requires go1.25 or later (file is go1.24) (govet)
🪛 GitHub Check: lint
internal/check/request_test.go
[failure] 598-598:
unnecessary trailing newline (whitespace)
internal/check/check.go
[failure] 456-456:
stdversion: sync.Go requires go1.25 or later (file is go1.24) (govet)
[failure] 440-440:
stdversion: sync.Go requires go1.25 or later (file is go1.24) (govet)
internal/check/request.go
[failure] 96-96:
avoid direct access to proto field t.Condition.Name, use t.GetCondition().GetName() instead (protogetter)
[failure] 95-95:
avoid direct access to proto field t.Condition, use t.GetCondition() instead (protogetter)
Co-authored-by: Joshua Jones <joshua.jones.software@gmail.com>
justincoh
left a comment
There was a problem hiding this comment.
In the near future I would love to see doc.go files in these internal packages like check and iterator. They are complex and it'd be nice to have a contextual entry point
Add support for exporting logs via OTLP, allowing integration with any OpenTelemetry-compatible backend (Grafana Loki, Datadog, GCP Cloud Logging, etc.). When log.otlp.enabled is set, logs are exported via OTLP in addition to stdout. The otelzap bridge attaches span context (trace ID, span ID) to each log record, enabling log-trace correlation in backends that support it. Configuration mirrors the trace configuration shape: the standard OTel env vars (OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_ENDPOINT) only answer where logs are sent, while the explicit OpenFGA flag (log.otlp.enabled / --log-otlp-enabled / OPENFGA_LOG_OTLP_ENABLED) answers whether to export at all — so a cluster that injects the generic endpoint variable for tracing does not silently start exporting logs on upgrade. zap's production sampling is applied outside the tee, so the keep/drop decision is made once, before fan-out: stdout and OTLP receive the same deterministically sampled stream and collector egress stays bounded during log storms. Changes: - Add otelzap bridge core backed by an OTLP log provider (internal/telemetry/logging.go) - Add WithOTELCore logger option that tees log entries to the bridge. The bridge core is capped to the configured log level, the stdout core strips the bridge-only context field via contextFilterCore, and the sampler wraps the tee (pkg/logger/logger.go) - Attach the context field in the *WithContext logging methods only when an OTEL core is configured, and use those methods in the gRPC logging interceptor so the bridge can extract span context (pkg/middleware/logging/logging.go) - Add config: log.otlp.enabled, log.otlp.endpoint, log.otlp.tls.enabled with OPENFGA_LOG_OTLP_* env vars and OTEL_EXPORTER_OTLP_* endpoint fallbacks (pkg/server/config/config.go, cmd/run/, .config-schema.json) feat: add potential v2 breaking change logs for Expand and ListUsers (openfga#3182) release: update changelog for release 1.18.1 (openfga#3188) release: Update changelog to prep for 1.18.1 release (openfga#3186) test: fix flaky TestV2CheckWithIteratorCache_HigherConsistencyBypassesCache (openfga#3061) Co-authored-by: Joshua Jones <joshua.jones.software@gmail.com> Merge commit from fork * test reproducing ListUsers report * implement fix * additional test with 3 operands fix: match IPv4-mapped IPv6 addresses in the in_cidr condition (openfga#3181) Signed-off-by: kanywst <niwatakuma@icloud.com> fix: use deterministic proto marshaling for stored authorization models (openfga#3171) chore: create draft release and publish after provenance succeeds for immutable tags/releases (openfga#3178) docs: fix changelog entry (openfga#3177) Signed-off-by: Saad Hussain <saad.hussain@okta.com> feat: add v2Check logs for resolution breaking change (openfga#3149) feat: BatchCheck uses v2Check when ExperimentalWeightedGraphCheck is enabled (openfga#3154) Signed-off-by: Saad Hussain <saad.hussain@okta.com> chore: update changelog to add CVE identifiers for recent fixes (openfga#3176) chore: update todo comment string in migration guide (openfga#3175) release: update changelog for release `v1.18.0` (openfga#3174) Co-authored-by: adriantam <adrian.tam@okta.com> Merge commit from fork * fix(authn): require issuer and audience when OIDC authn is enabled Enforce configuration authn.oidc.audience and authn.oidc.issuer when `authn.method` is set to `oidc`. * fixed based on code review feedback * fix: adding comments + test case on empty space for audience * update based on code review feedback * Update CHANGELOG.md Co-authored-by: Joshua Jones <joshua.jones.software@gmail.com> * update changelog * Update CHANGELOG.md --------- Co-authored-by: Joshua Jones <joshua.jones.software@gmail.com> Merge commit from fork * fix(mysql): collate identifier columns as utf8mb4_bin * fix: add operator note to changelog * fix: move tests to shared storage * fix: separate migrations for each table * fix: add runbook for migrations * fix: set lock_wait_timeout * fix: add docker instructions * fix: add docker instructions * fix: combine the migrations back into one, fix documentation for this * fix: include details about table copy and * Update CHANGELOG.md Co-authored-by: Adrian Tam <adrian.tam@okta.com> --------- Co-authored-by: Adrian Tam <adrian.tam@okta.com> fix: use constant-time comparison for preshared key authentication (openfga#3168) chore(deps): bump the dependencies group with 2 updates (openfga#3166) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Justin Cohen <justincoh@gmail.com> chore(deps): bump the dependencies group with 2 updates (openfga#3167) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> release: update changelog for release `v1.17.1` (openfga#3165) Signed-off-by: Saad Hussain <saad.hussain@okta.com> chore: bump grpc-health-probe to v0.4.52 (openfga#3164) Co-authored-by: Saad Hussain <saad.hussain@okta.com> docs: update caching docs (openfga#3163) Signed-off-by: Saad Hussain <saad.hussain@okta.com> fix: continuation token deserializer - handle `|` in type names (openfga#3152) chore(deps): bump the dependencies group across 1 directory with 13 updates (openfga#3162) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Justin Cohen <justincoh@gmail.com> chore(deps): bump the dependencies group across 1 directory with 10 updates (openfga#3156) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Justin Cohen <justincoh@gmail.com> fix: use query start time as iterator cache entry LastModified to prevent stale-read survival (openfga#3155) chore(deps): bump grpc-ecosystem/grpc-health-probe from v0.4.50 to v0.4.51 in the dependencies group (openfga#3157) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Saad Hussain <saad.hussain@okta.com> Co-authored-by: Justin Cohen <justincoh@gmail.com> chore: Bump go toolchain to 1.26.4 (openfga#3159) fix: prevent v2Check from falling back for throttling and validation (openfga#3150) ci: make pr benchmark comparisons less fragile (openfga#3153) Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> release: update changelog for release `v1.17.0` (openfga#3151) Co-authored-by: Vic-Dev <vmichellej@gmail.com> refactor: redesign cache key generation, making it more secure and consistent (openfga#3148) feat: add configurable trace sampler with ParentBased support (openfga#3072) release: update changelog for release `v1.16.1` (openfga#3147) chore: update grpc-health-probe to latest to addres std lib CVEs (openfga#3146) fix: skip v2Check weight2 pruning if iterator is unordered (openfga#3145) Signed-off-by: Saad Hussain <saad.hussain@okta.com> chore: add concurrency-group to PR-related CI steps (openfga#3140) docs: move OIDC JWKS refresh entry from v1.15.1 to v1.16.0 (openfga#3142) fix: when v2Check is primary algorithm, fix fallback condition and emit metrics (openfga#3141) release: update changelog for release `v1.16.0` (openfga#3139) Merge commit from fork * fix: Standardize cache key generation everywhere * Revert "fix: Standardize cache key generation everywhere" This reverts commit ce60c6f9ae48a310a4dea2b824b5993520c8ba1c. * length-encoded approach * Revert "length-encoded approach" This reverts commit b94637c187c57a2d3f3904247267bbc8ad21a41a. * implement Hexer() and use in some cache keys * appendConditionsHash -> generateConditionsHash * mv Hexer -> BuildKey * BuildKey -> BuildCacheKey refactor to BuildCacheKey on each individual prefix component to remove collision risk * make v2 cacheKey functions the standard * make cache key prefixes consistent * generateConditionsHash -> generateConditionsString * delimit condition name string * remove extra empty check * relocate and reuse existing userTypeRestrictions function * replace hashing of conditions for brevity * fix tests * cleanup, add test file * remove unused slice capacity * make prefix treatments consistent * add nil-byte separator in object ids * hash user type restrictions for brevity * update comment string, use nil-byte separator for future-proofing * remove call to xxhash.new * adjustments for performance, consistency, and interface ergonomics. * make condition hash generation more efficient. * add clarifying comments for size caluculations * fix builder growth calculations to match original intentions * update outdated comment * don't hash potentially zero values * Revert "don't hash potentially zero values" This reverts commit 2ad6c5b796bc20a82c1414c5146c3c708330eefa. * add condition key separator unconditionally * patch test expectations for new key structure * export V2IteratorCachePrefix, use in tests --------- Co-authored-by: justin <justin.cohen@okta.com> Co-authored-by: Joshua Jones <joshua.jones@okta.com> fix: unintentional zeroing of slice values by setting slice to nil (openfga#3135) increase the check v2 trace information fidelity (openfga#3134) feat: add datastore ping and ping retry configurations (openfga#3113) fix: prevent v2Check strategies returning spurious false on context cancellation (openfga#3128) Signed-off-by: Saad Hussain <saad.hussain@okta.com> fix(authn/oidc): refresh JWKS on unknown kid to handle key rotation (openfga#3101) fix: v2Check falls back to v1 on errors (openfga#3126) fix: don't cache false results from cancelled-context goroutines in v2Check (openfga#3125) Signed-off-by: Saad Hussain <saad.hussain@okta.com> make union cache key unique by including the node input's label (openfga#3117) Signed-off-by: Saad Hussain <saad.hussain@okta.com> Co-authored-by: Saad Hussain <saad.hussain@okta.com> fix: shadow v2check and check use the same trace (openfga#3118) fix: bump go toolchain version to 1.26.3 (openfga#3115) chore: add more spans/attributes to v1 and v2 Check (openfga#3116) Signed-off-by: Saad Hussain <saad.hussain@okta.com> fix: add matches attribute to shadowv2Check and Check spans (openfga#3114) fix: use the same `request_id` for shadow traces in v2Check (openfga#3110) release: update changelog for release `v1.15.1` (openfga#3112) feat: reuse MySQL container across tests (openfga#3042) fix: close all channels opened thus far, before return on error (openfga#3111) chore: Add more spans/attributes to v2Check (openfga#3102) Signed-off-by: Saad Hussain <saad.hussain@okta.com> fix: ensure acquired limiter token is released on throttle context cancelation (openfga#3106) fix: cancel context before waiting on worker pool in ResolveUnionEdges (openfga#3105) fix: replace golang.org/x/exp/maps with stdlib maps to resolve govet inline errors (openfga#3104) fix: v2Check EdgeCacheKey collisions (openfga#3097) Signed-off-by: Saad Hussain <saad.hussain@okta.com> fix: expose context errors when they can be the potential cause of an underlying datastore error (openfga#3096) chore(deps): bump the dependencies group across 1 directory with 2 updates (openfga#3093) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fix: move subproblem cache lookup from ResolveUnionEdges into ResolveUnion (openfga#3095) chore(deps): bump the dependencies group with 4 updates (openfga#3092) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fix: error handler panic (openfga#3091) release: update changelog for release `v1.15.0` (openfga#3090) chore(deps): bump the dependencies group across 1 directory with 4 updates (openfga#3087) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fix: v2Check correctly uses query cache even when cache controller is disabled (openfga#3086) Signed-off-by: Saad Hussain <saad.hussain@okta.com> chore(deps): bump the dependencies group across 1 directory with 7 updates (openfga#3081) Signed-off-by: dependabot[bot] <support@github.com> chore(deps): bump github.com/jackc/pgx/v5 from 5.9.1 to 5.9.2 (openfga#3085) Signed-off-by: dependabot[bot] <support@github.com> chore(deps): bump grpc-ecosystem/grpc-health-probe from v0.4.47 to v0.4.48 in the dependencies group across 1 directory (openfga#3065) Signed-off-by: dependabot[bot] <support@github.com> feat: try to use UDS internally between HTTP server and gRPC server (openfga#2937) feat: add jitter to internal cache TTLs to prevent thundering herd effects (openfga#3033) Signed-off-by: Asish Kumar <officialasishkumar@gmail.com> list objects pipeline edge pruning (openfga#3075) update toolchain go to 1.26.2 to address stdlib CVEs (openfga#3084) chore: add store ID and datastore query/item count to shadowV2Check log (openfga#3073) Signed-off-by: Saad Hussain <saad.hussain@okta.com> CI speed up (openfga#3062) Co-authored-by: Joshua Jones <joshua.jones.software@gmail.com> Co-authored-by: Joshua Jones <joshua.jones@okta.com> Add tracing to v2 Check planner strategy selection (openfga#3077) fix: v2Check honours `check-query-cache-enabled` flag (openfga#3070) Signed-off-by: Saad Hussain <saad.hussain@okta.com> feat: reuse PostgreSQL container across tests (openfga#3018) chore: fix reporter links in changelog (openfga#3069) release: update changelog for release `v1.14.2` (openfga#3068) fix: add null byte delimiter in contextual tuple cache keys and validation in v2Check (openfga#3064) release: update changelog for release `v1.14.1` (openfga#3060) chore(deps): bump the dependencies group across 1 directory with 7 updates (openfga#3056) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> fix: use a baseURL for AuthZEN configuration endpoint (openfga#3057) chore: replace docker with moby (openfga#3047) Iterator Cache V2: Storage Wrapper Pattern with Lock-Free Design (openfga#3016) Signed-off-by: Saad Hussain <saad.hussain@okta.com> Co-authored-by: Saad Hussain <saad.hussain@okta.com> Co-authored-by: Joshua Jones <joshua.jones.software@gmail.com> test: fix flaky condition test (openfga#3058) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> chore(deps): bump the dependencies group across 1 directory with 6 updates (openfga#3045) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp from 1.41.0 to 1.43.0 (openfga#3054) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> feat: add graceful shutdown timeout configuration (openfga#2976) chore: update changelog to reflect playground off-by-default behavior (openfga#3053) chore: decouple Error() and Unwrap() (openfga#3051) perf: remove `fmt` from cache key generation (openfga#3006) perf: enhancements for list objects (openfga#3043) chore: remove stale notice file (openfga#3050) docs: update 1.14.0 for CVE fix (openfga#3046) feat: Add datastore throttling & concurrency limiting to v2Check (openfga#3035) Signed-off-by: Saad Hussain <saad.hussain@okta.com> release: update changelog for release `v1.14.0` (openfga#3040) batch check cache (openfga#3025) Merge commit from fork Also prevent enabling playground when the server requires authentication feat: add stats on tuple iterator query (openfga#3030) fix: remove unnecessary non-deterministic test (openfga#3038) remove unnecessary import (openfga#3032) perf: improve the intersection algorithm, reducing latency and memory use (openfga#3031) fix: ListObjects pipeline algorithm enhancements and fix for potential deadlock (openfga#3028) chore: Also update openfga/helm-charts in release script (openfga#3010) chore: update CICD to enforce GRPC healthprobe changes (openfga#2990) fix: SQL `TupleOperation` serialization and `pgx.ErrNoRows` error handling (openfga#3014) docs: update changelog for CVE-2026-33729 (openfga#3017) chore: output the diff after running keep-a-changelog (openfga#3015) chore(deps): bump the dependencies group across 1 directory with 11 updates (openfga#2998) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> chore(deps): bump the dependencies group across 1 directory with 10 updates (openfga#2999) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> release: update changelog for release `v1.13.1` (openfga#3002) Merge commit from fork * strip unicode control characters, encode context key length in cache key * add cache_key tests to ensure we escape control characters and encode context key length * run linter * docs: update changelog * Apply suggestion from @adriantam Co-authored-by: Adrian Tam <adrian.tam@okta.com> * Apply suggestion from @adriantam Co-authored-by: Adrian Tam <adrian.tam@okta.com> --------- Co-authored-by: Saad Hussain <saad.hussain@okta.com> Co-authored-by: Saad Hussain <saad.h@outlook.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> release: update changelog for release `v1.13.0` (openfga#2997) refactor: Separate caches for v1 and v2 Check (openfga#2968) Co-authored-by: Justin Cohen <justincoh@gmail.com> docs: fix typos in comments (openfga#2972) Signed-off-by: Artem Muterko <artem@sopho.tech> Co-authored-by: Adrian Tam <adrian.tam@okta.com> observability: aggregate message statistics for each list-objects sender into a single span (openfga#2993) Co-authored-by: Justin Cohen <justincoh@gmail.com> fix: capture panics in pipeline's base resolver, and return as errors. (openfga#2994) docs: fix typos in RELEASES.md and Makefile (openfga#2980) Co-authored-by: Adrian Tam <adrian.tam@okta.com> AuthZen v1.0 Implementation (openfga#2875) Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Rokibul Hasan <mdrokibulhasan@appscode.com> Signed-off-by: Vihang Mehta <vihang@gimletlabs.ai> Co-authored-by: Karl Persson <kalle.persson92@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Maria Ines Parnisari <maria.inesparnisari@okta.com> Co-authored-by: Rokibul Hasan <mdrokibulhasan18@gmail.com> Co-authored-by: José Padilla <jose.padilla@okta.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> Co-authored-by: Vihang Mehta <vihang@gimletlabs.ai> Co-authored-by: Joshua Jones <joshua.jones.software@gmail.com> Co-authored-by: Justin Cohen <justincoh@gmail.com> release: update changelog for release `v1.12.1` (openfga#2992) chore(deps): bump google.golang.org/grpc from 1.79.1 to 1.79.3 (openfga#2988) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> chore: enforce minor version upgrade rule (openfga#2978) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> perf: tuple validation performance tweaks (openfga#2984) fix: support URI schemes in OTLP endpoint configuration (openfga#2981) refactor: remove custom pipes and replace with channels (openfga#2977) chore(docs): added caching docs (openfga#2664) Co-authored-by: Saad Hussain <saad.hussain@okta.com> release: update changelog for release `v1.12.0` (openfga#2974) chore: update toolchain go to 1.26.1 (openfga#2975) fix: update toolchain go to 1.25.8 to address stdlib CVEs (openfga#2971) fix: correct swapped format args in DecodeParameterType error message (openfga#2961) Signed-off-by: Artem Muterko <artem@sopho.tech> Co-authored-by: Adrian Tam <adrian.tam@okta.com> perf: small tweaks to tuple validation functions (openfga#2963) Co-authored-by: Vic-Dev <vmichellej@gmail.com> test: add tests for condition parameter type any with complex context structures (openfga#2959) Signed-off-by: Artem Muterko <artem@sopho.tech> test: add functional test for ReadAssertions API endpoint (openfga#2960) Signed-off-by: Artem Muterko <artem@sopho.tech> Co-authored-by: Adrian Tam <adrian.tam@okta.com> make configurable the maximum received grpc message size (openfga#2952) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> fix: set ExperimentalPipelineListObjects in experimentals by default (openfga#2957) fix: `cache_item_count` metric overcounting (openfga#2950) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> chore(deps): bump the dependencies group across 1 directory with 7 updates (openfga#2953) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> refactor: Make shadowV2Check final log clearer (openfga#2946) Move telemetry package to internal/telemetry (openfga#2938) Signed-off-by: Oleksandr Shestopal <ar.shestopal-oshegithub@gmail.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> chore(deps): bump the dependencies group across 1 directory with 3 updates (openfga#2956) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fix: Resolve race condition in check reducers (openfga#2947) Co-authored-by: Adrian Tam <adrian.tam@okta.com> fix: gateway grpc client tls cert rotation (openfga#2951) Signed-off-by: Shashank Goel <goelshashank13@gmail.com> fix: disable LO pipeline if ff has it set for a store (openfga#2945) chore(deps): bump the dependencies group across 1 directory with 4 updates (openfga#2949) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fix: various random fixes (openfga#2942) refactor: check using weighted graph (openfga#2816) Co-authored-by: Yissell Garma <yissell.garma@okta.com> release: update changelog for release `v1.11.6` (openfga#2939) feat: enable pipeline algorithm by default (openfga#2921) add tests to exercise reported bug (openfga#2934) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Adrian Tam <adrian.tam@okta.com> refine AGENTS.md to make it more concise with higher value. (openfga#2936) Co-authored-by: Justin Cohen <justincoh@gmail.com> migrate grpc dial context to NewClient (openfga#2714) Co-authored-by: Adrian Tam <adrian.tam@okta.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> chore(deps): bump filippo.io/edwards25519 from 1.1.0 to 1.1.1 (openfga#2935) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Description
What problem is being solved?
How is it being solved?
What changes are made to solve it?
References
Review Checklist
mainSummary by CodeRabbit
New Features
Tests
Bug Fixes / Changes
✏️ Tip: You can customize this high-level summary in your review settings.