Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: in Check, if source and target types aren't connected, return allowed=false #1582

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

miparnisari
Copy link
Member

@miparnisari miparnisari commented Apr 29, 2024

Description

Adding a performance optimization for queries of the form Check(user, relation, object). If user type and object types are disconnected/unreachable, we return early with allowed=false.

I achieve this by first building the graph of the model using most of the code from https://github.com/jon-whit/openfga-graphviz-gen.

Testing

With this model

model
  schema 1.1

type user

type user2

type folder
  relations
    define viewer: [user2]

type doc
  relations
    define can_read: viewer or viewer from parent
    define parent: [folder]
    define viewer: [user2]

And this tuple: doc:budget # parent @ folder:parent

Before

image

After

image

@miparnisari miparnisari changed the title perf(Check): is source and target types aren't connected, exit early perf(Check): if source and target types aren't connected, exit early Apr 29, 2024
Copy link

codecov bot commented Apr 29, 2024

Codecov Report

Attention: Patch coverage is 93.29897% with 13 lines in your changes are missing coverage. Please review.

Project coverage is 86.46%. Comparing base (bb8a1e0) to head (26969c7).
Report is 16 commits behind head on main.

Files Patch % Lines
pkg/typesystem/check_graph.go 93.96% 5 Missing and 4 partials ⚠️
internal/graph/check.go 91.31% 1 Missing and 1 partial ⚠️
pkg/typesystem/typesystem.go 90.91% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1582      +/-   ##
==========================================
+ Coverage   86.20%   86.46%   +0.26%     
==========================================
  Files          87       88       +1     
  Lines        8183     8406     +223     
==========================================
+ Hits         7053     7267     +214     
- Misses        797      801       +4     
- Partials      333      338       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -615,6 +629,28 @@ func (c *LocalChecker) ResolveCheck(
return resp, nil
}

// areTypesConnected returns true if it is possible to reach objectType#relation starting from userType[#userRelation].
func (c *LocalChecker) areTypesConnected(typesys *typesystem.TypeSystem, user, userType, userRelation, objectType, relation string) (bool, error) {
g := New(typesys)
Copy link
Member

Choose a reason for hiding this comment

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

Why?

Copy link
Member Author

Choose a reason for hiding this comment

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

Why what? 😆

sourceRel = typesystem.DirectRelationReference(userType, userRelation)
}

edges, err := g.GetRelationshipEdges(typesystem.DirectRelationReference(objectType, relation), sourceRel)
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps the fast follow is improving the typesystem package, particularly as this object lives in memory for a long period of time, calculating this multiple times would be waste of compute that could be incrementally cached lazily.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we should definitely calculate the edges up front once and not in every sub-problem resolution... however, that wasn't a trivial change so I didn't do it

Copy link
Member

Choose a reason for hiding this comment

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

This needs to happen before we merge this PR. Graph construction from the typesystem in critical Check resolution code is a "no go" right now IMO.

Let's fix up the typesystem, do better lazy compute of the type graph, and then let's merge this work in.

Copy link
Member Author

Choose a reason for hiding this comment

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

@elbuo8 @jon-whit I took a stab at computing the graph once (mostly copied over from https://github.com/jon-whit/openfga-graphviz-gen/) and then using BFS to get the connectivity. I didn't reuse the getRelationshipEdges code because that was harder for me to adapt than writing this code. I think this code is more intuitive because you can visualize it easily. Let me know what you think! We can also extend it to compute cycles way faster than today (using https://github.com/jon-whit/openfga-graphviz-gen/blob/main/writer.go#L140)

Note that this PR adds more CPU and memory consumption during model validation. But again, this can be mitigated by changing the cycle detection code which is currently very inefficient as a fast follow-up.

@miparnisari miparnisari changed the title perf(Check): if source and target types aren't connected, exit early perf: in Check, if source and target types aren't connected, return allowed=false May 3, 2024
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.

None yet

3 participants