Skip to content

Conversation

yissellokta
Copy link
Contributor

@yissellokta yissellokta commented Sep 10, 2025

To simplify graph operations, the system now introduces logical nodes (LogicalUsersetNode, LogicalTTUNode) to group related edges together. 🤝

Previously, multiple direct user assignments or multiple "tuple-to-userset" (TTU) edges were treated individually, which complicated algorithms, especially during algebraic operations.

Now, these new "union" nodes are created dynamically to consolidate multiple edges into a single logical unit, significantly reducing complexity for core processes like Check and ListObjects.

Description

The Challenge: Simplifying Complex Edge Relationships
The previous weighted graph model did not inherently group multiple direct userset assignments or multiple "tuple-to-userset" (TTU) edges, even when they originated from the same definition. This created significant implementation challenges for core algorithms in OpenFGA, especially when performing algebraic operations. Essentially, the system failed to treat multiple direct edges or multiple TTU edges as a single logical union, unnecessarily increasing complexity.

The Solution: Introducing Logical Nodes and Edges
To reduce complexity in the Check, ListObjects, and indexer compilation algorithms, we are introducing two new types of "union" nodes and two corresponding "rewrite" edges. These new structures help group related edges into single, manageable units.

New Node Types:
LogicalUsersetNode: Consolidates multiple direct user assignments.
LogicalTTUNode: Consolidates multiple TTU edges that belong to the same definition.

New Edge Types:
DirectLogicalEdge: A rewrite edge connecting to a LogicalUsersetNode.
TTULogicalEdge: A rewrite edge connecting to a LogicalTTUNode.

How It Works: Creation Logic
These new logical nodes are created dynamically under specific conditions:

A LogicalUsersetNode is created when an algebraic operation involves more than one direct edge. All relevant direct edges are then extracted and connected to this new logical node.

A LogicalTTUNode is created when an algebraic operation involves more than one TTU edge from the same definition. These TTU edges are then extracted and grouped under the new logical node.

What problem is being solved?

References

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

  • New Features
    • Introduces logical grouping nodes for userset and tuple-to-userset relations, yielding clearer intermediate structure in graphs.
    • Adds logical edge types to represent these groupings.
    • Graphs may display additional intermediate nodes/edges where multiple relations are grouped.
  • Refactor
    • Unifies handling of unions, cycles, and weight assignment across logical operators for consistent behavior.
    • Error messages updated to reference logical userset/TTU scenarios.
  • Tests
    • Updated and expanded tests to validate new logical nodes and adjusted graph sizes and structures.

@yissellokta yissellokta requested review from a team as code owners September 10, 2025 18:44
Copy link

coderabbitai bot commented Sep 10, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds new logical node and edge types, updates weighted-graph algorithms to treat them as logical/union operators, and modifies the builder to insert grouping nodes (LogicalUserset, LogicalTTU) that consolidate multiple related edges. Tests are updated to reflect new intermediate nodes, edge routing, and revised error messages.

Changes

Cohort / File(s) Summary
Edge types
pkg/go/graph/graph_edge.go
Adds DirectLogicalEdge (4) and TTULogicalEdge (5) to EdgeType. Attributes() unchanged; new types fall through to default.
Node types
pkg/go/graph/graph_node.go
Adds LogicalUserset (4) and LogicalTTU (5) to NodeType; comments/examples added.
Weighted graph algorithms
pkg/go/graph/weighted_graph.go
Introduces isLogicalOperator and isLogicalUnionOperator; updates AssignWeights and cycle/union checks to use them. Reworks calculateNodeWeightWithMixedStrategy (requires exactly two edges, merge by max). Simplifies calculateNodeWeightWithEnforceTypeStrategy to derive/prune rewrite weights from edges. Updates validation to permit logical unions.
Graph builder (grouping nodes)
pkg/go/graph/weighted_graph_builder.go
Adds LogicalTTU and LogicalUserset grouping nodes when multiple related entries exist. Routes parent→group via TTULogicalEdge/DirectLogicalEdge; downstream edges originate from grouping node. Adjusts HasEdge/UpsertEdge sources and labels accordingly.
Tests
pkg/go/graph/weighted_graph_builder_test.go
Updates expectations: additional nodes/edges due to LogicalUserset; verifies presence and outgoing edges from logical nodes; adjusts error assertions and model formatting.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant P as Parent Node
  participant B as Builder
  participant LU as LogicalUserset (group)
  participant LT as LogicalTTU (group)
  participant C as Child Nodes

  rect rgba(90,120,180,0.08)
  note over B: Direct relation grouping
  B->>B: Detect multiple directlyRelated entries
  B->>LU: Create LogicalUserset node
  P-->>LU: Upsert DirectLogicalEdge
  LU-->>C: Upsert DirectEdge to each target
  end

  rect rgba(120,90,180,0.08)
  note over B: TTU relation grouping
  B->>B: Detect multiple type tupleset relations
  B->>LT: Create LogicalTTU node
  P-->>LT: Upsert TTULogicalEdge (labeled by tupleset relation)
  LT-->>C: Upsert TTUEdge to each target
  end
Loading
sequenceDiagram
  autonumber
  participant WG as WeightedGraph
  participant N as Node
  participant E as Edges

  rect rgba(90,180,120,0.08)
  note over WG: AssignWeights / traversal
  WG->>N: Visit node
  alt isLogicalOperator(N)
    WG-->>N: Defer weight assignment
  else
    WG->>E: calculateNodeWeightFromTheEdges
    alt isLogicalUnionOperator(N)
      WG-->>N: Union-based cycle/weight handling
    else
      WG-->>N: Standard edge-based weights
    end
  end
  end

  rect rgba(180,140,90,0.08)
  note over WG: Strategies
  WG->>N: MixedStrategy
  N->>E: Require exactly two edges
  E-->>N: Merge weights by key (max)

  WG->>N: EnforceTypeStrategy
  E-->>N: Derive rewriteWeights from common keys across edges
  WG-->>N: Assign pruned rewriteWeights
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • adriantam
  • jpadilla

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title appears to mirror the branch name rather than succinctly summarizing the primary change; while it mentions “grouping userset ttus,” it is ambiguous and does not clearly convey that the PR introduces new logical grouping nodes and rewrite edges for userset and TTU relationships. Consider renaming the PR to clearly reflect the main feature, for example: “Add logical grouping nodes and rewrite edges for userset and TTU relationships.”
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/go/graph/weighted_graph_builder.go (1)

144-159: Remove conditions from TTU edges
Replace the condition argument with an empty string so TTU edges remain condition-agnostic:

- wg.UpsertEdge(node, nodeSource, TTUEdge, typeTuplesetRelation, relatedType.GetCondition())
+ // TTU edges represent rewrite hops; conditions live on direct relation edges.
+ wg.UpsertEdge(node, nodeSource, TTUEdge, typeTuplesetRelation, "")
🧹 Nitpick comments (8)
pkg/go/graph/graph_node.go (1)

15-16: Tighten comments; fix typo ("wherer") and clarify examples.

Small doc nits only; enums look fine. Recommend correcting the TTU comment and making both examples consistent.

Apply this diff:

-	LogicalUserset          NodeType = 4 // e.g. `[user, employee, type1#rel]`
-	LogicalTTU              NodeType = 5 // e.g. `member from parent, wherer parent can have multiple terminal types`
+	LogicalUserset          NodeType = 4 // e.g., `[user, employee, type1#rel]`
+	LogicalTTU              NodeType = 5 // e.g., `viewer from parent`, where parent can resolve to multiple terminal types

Optional: add a String() for NodeType (or use stringer) to improve logs/graph dumps.

pkg/go/graph/graph_edge.go (1)

11-16: Add DOT attributes for new logical edges
DirectLogicalEdge and TTULogicalEdge are emitted by the builder but currently fall through to an empty Attributes() case—update the EdgeType Attributes() method in pkg/go/graph/graph_edge.go to return distinct labels (e.g. "logical" and "logical-ttu").

pkg/go/graph/weighted_graph_builder_test.go (2)

601-603: Avoid brittle total node/edge counts; assert structure instead.

Grouping nodes will keep evolving; fixed totals tend to break. Prefer asserting presence of key nodes/edges/weights (e.g., LogicalUserset node exists, edge types/conditions correct) rather than require.Len(graph.nodes, 13) and require.Len(graph.edges, 9).

Example:

// Assert at least one LogicalUserset exists and conditions merged:
found := false
for _, n := range graph.nodes {
	if n.nodeType == LogicalUserset {
		found = true
		break
	}
}
require.True(t, found)
```<!-- review_comment_end -->

---

`1222-1222`: **Reduce reliance on global node/edge counts across intersection suites.**

The added `require.Len(graph.nodes, X)` and `require.Len(graph.edges, Y)` are fragile with grouping changes. Keep assertions that truly define correctness (weights, types, operator labels), and drop or relax these totals.


Example pattern:

```go
// Instead of total counts:
andEdge := graph.edges["document#viewer"][0]
require.Equal(t, OperatorNode, andEdge.to.nodeType)
require.Equal(t, "intersection", andEdge.to.label)
require.Equal(t, expected, andEdge.GetWeights())
```<!-- review_comment_end -->



Also applies to: 1247-1247, 1270-1270, 1396-1396, 1489-1491, 1514-1516

</blockquote></details>
<details>
<summary>pkg/go/graph/weighted_graph.go (4)</summary><blockquote>

`132-136`: **Deferring logical nodes during initial pass is fine; fix typo**

Typo: “Inititally” → “Initially”.

---

`153-160`: **Helper for logical-operator detection is correct; fix minor typos**

Typos in comments: “asignations” → “assignments”.

---

`161-168`: **Helper for union-like logical operators is correct; fix minor typos**

Same typo note as above.

---

`467-486`: **Cycle handling for union-like logical nodes: LGTM; fix typo**

Typo: “depencies” → “dependencies”.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 222b2371c53f943473b90a323c3dd3d3ec7b92a9 and 2e7e449a39fab2a75b8913ab42d37fa25857ab43.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `pkg/go/graph/graph_edge.go` (1 hunks)
* `pkg/go/graph/graph_node.go` (1 hunks)
* `pkg/go/graph/weighted_graph.go` (8 hunks)
* `pkg/go/graph/weighted_graph_builder.go` (5 hunks)
* `pkg/go/graph/weighted_graph_builder_test.go` (12 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code graph analysis (3)</summary>

<details>
<summary>pkg/go/graph/weighted_graph_builder.go (2)</summary><blockquote>

<details>
<summary>pkg/go/graph/graph_node.go (3)</summary>

* `SpecificTypeAndRelation` (12-12)
* `LogicalTTU` (16-16)
* `LogicalUserset` (15-15)

</details>
<details>
<summary>pkg/go/graph/graph_edge.go (4)</summary>

* `TTULogicalEdge` (16-16)
* `TTUEdge` (13-13)
* `DirectLogicalEdge` (15-15)
* `DirectEdge` (11-11)

</details>

</blockquote></details>
<details>
<summary>pkg/go/graph/weighted_graph_builder_test.go (3)</summary><blockquote>

<details>
<summary>pkg/go/graph/graph_node.go (1)</summary>

* `LogicalUserset` (15-15)

</details>
<details>
<summary>pkg/go/transformer/dsltojson.go (1)</summary>

* `MustTransformDSLToProto` (553-560)

</details>
<details>
<summary>pkg/go/graph/weighted_graph_builder.go (1)</summary>

* `NewWeightedAuthorizationModelGraphBuilder` (19-21)

</details>

</blockquote></details>
<details>
<summary>pkg/go/graph/weighted_graph.go (2)</summary><blockquote>

<details>
<summary>pkg/go/graph/weighted_graph_node.go (1)</summary>

* `WeightedAuthorizationModelNode` (3-11)

</details>
<details>
<summary>pkg/go/graph/graph_node.go (5)</summary>

* `OperatorNode` (13-13)
* `LogicalTTU` (16-16)
* `LogicalUserset` (15-15)
* `UnionOperator` (18-18)
* `SpecificTypeAndRelation` (12-12)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>pkg/go/graph/weighted_graph_builder_test.go (2)</summary><blockquote>

`1203-1216`: **Non-functional DSL formatting only.**

These model string edits are cosmetic; no issues. 
<!-- review_comment_end -->



Also applies to: 1231-1241, 1260-1264, 1280-1291, 1303-1314, 1329-1340, 1354-1365, 1379-1390, 1404-1415, 1473-1484, 1498-1508

---

`1445-1445`: **Good switch to ErrorContains for message assertions.**

Less brittle than exact equality; keeps tests resilient to wording tweaks.
<!-- review_comment_end -->


Also applies to: 1465-1465

</blockquote></details>
<details>
<summary>pkg/go/graph/weighted_graph_builder.go (3)</summary><blockquote>

`124-133`: **TTU logical-grouping creation looks good**

Condition and labeling are sensible; node reuse via a deterministic uniqueLabel is fine.

---

`182-189`: **Direct logical-grouping creation looks good**

Creation gate (non-STR parent + >1 directlyRelated) and edge type are correct.

---

`209-209`: **Direct edge upsert with condition is correct**

De-dupes and aggregates conditions as expected.

</blockquote></details>
<details>
<summary>pkg/go/graph/weighted_graph.go (2)</summary><blockquote>

`456-466`: **Cycle handling for non-logical nodes: LGTM**

Branching to max-strategy when not logical is consistent.

---

`573-574`: **Intersection strategy assignment: LGTM**

Directly assigning rewriteWeights is correct.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@yissellokta yissellokta force-pushed the feat/grouping_userset_ttus branch from 3aa835c to 360c743 Compare September 12, 2025 18:49
@justincoh
Copy link

@yissellokta is it accurate to say that DirectLogicalGrouping is only created from an operator node? Meaning:

# This will just create 3 regular direct edges
define rel1: [user, employee, other] 

# This Union will have two edges. One Direct to rel3 and one DirectLogicalEdge to a
# DirectLogicalGrouping, which then has 3 Direct edges
define rel2: [user, employee, other] or rel3

justincoh
justincoh previously approved these changes Sep 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants