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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions app/spicedb/modeling/recursion-and-max-depth/page.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Callout } from "nextra/components";

# Cyclical Relationships and Traversal Limits

SpiceDB answers permissions questions by traversing a **tree** constructed from your [schema] (structure) and [relationships] (data).
Expand Down Expand Up @@ -188,6 +190,106 @@ If the subject is found before the traversal hits the cycle or exceeds the depth

However, if the subject is **not** found, the traversal continues until it hits the depth limit and returns an error.

### What do I do about a max depth error on LookupResources?

_Note: the following debug behavior was added in SpiceDB 1.52.0_

Consider the following validation file:

```yaml
schema: |-
definition user {}

definition folder {
relation viewer: user
relation parent: folder
permission view = parent->view + viewer
}

definition resource {
relation folder: folder
permission view = folder->view
}
relationships: |-
folder:a#viewer@user:someuser
folder:a#parent@folder:b
folder:b#parent@folder:a
```

Note that the `parent` relation on the `folder` definition is recursive, and that we've written a `parent` relationship from `folder:a` to `folder:b` and vice versa, creating a cycle.

If you run `zed import` on this file, and then run `zed permission lookup-resources resource view user:someuser`, you'll get a `max depth exceeded` error:

```
rpc error: code = FailedPrecondition desc = max depth exceeded: this usually indicates a recursive or too deep data dependency.
```

This indicates that either a branch was too deep or a cycle was encountered as SpiceDB was walking the set of reachable resources. You can re-run the request with `--debug` to get a stack trace that shows the cycle:

```sh
zed permission lookup-resources resource view user:someuser --debug
```

You'll get output that looks like this:

```
The following resource/relation pairs were found in a cycle in LookupResources:

- folder:a#view

You will need to delete a relationship to break the cycle.
To find the pairs involved in the cycle, issue a check from the resource to itself across the relation.
For example:

zed permission check folder:a view folder:a --explain

```
Comment on lines +233 to +246
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i feel iffy about pasting the literal error here, which can change in the future. why not just say "use --debug which will give you more information"?

and the following paragraphs are more about zed permission check --explain than about LR 😅


If you call `zed permission check folder:a view folder:a --explain`, you'll get output like the following:

```
! folder:a view (8.434693ms)
└── ! folder:b view (8.143951ms)
└── ! folder:a view (cycle) (7.970042ms)
```

Some notes on reading the output:

- The lines should be read as resources with the relation connecting them to the previous pair. For example, the line marked by `(cycle)` can be read as "there's a `view` relation between `folder:a` and `folder:b`."
- The relation can be a schema relation, a schema permission, or a schema subject relation. Notice that in the line marked by `cycle`, the relation is `view`, corresponding to the `view` permission on the `folder` definition in the schema. This means that you might need to do some translation to connect the relation in a line to a concrete relationship.
- The `(cycle)` marks the endpoints where a resource that is a member of a cycle was seen for the second time. It does _not_ mean that the marked relation is the offending relation, just that everything between the marks is a member of the cycle. You'll need to think about which relationships are written where to understand what needs to change in your business logic to avoid the cycle.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the other two bullets feel useless to me; i'd get rid of them. the third is the bullet that is important.


In this case, we can see in the output that `folder#view` is going back and forth between `folder:a` and `folder:b`, and from the permission definition we can see that `folder#view` is defined in terms of `viewer` and `parent`. `viewer` can't be a part of a cycle, because it points at a `user` and there's no outgoing relation from the `user definition.` This means that `parent` is the offending relation, and we can remove one of the `folder#parent` relationships to break the cycle.

#### Limitations

##### Schema Structure

If your schema has certain structures, calling `zed permission check --explain` may return "no permission" rather than showing you a cycle. For example, if we changed the folder definition in the schema above to the following:

```
definition folder {
relation viewer: user
relation editor: user
relation folder: parent
permission view = (viewer + parent->view) & editor
}
```

to indicate that the user must also be an editor in order to view, `folder:a#view@folder:a` can never be satisfied, because it can never satisfy the `& editor` clause of the permission. Intersection arrows and certain negations may result in similar behavior.

One potential workaround is to make a check with the desired terminal subject (usually a `user`) on that object:

```sh
zed permission check folder:a view user:someuser --explain
```

This will work if the permission walk enters the cycle and then cannot exit, but will potentially terminate normally if there's a permission path that terminates after branching off of the cycle.

##### Caveats

If your schema has caveats and there are caveats along the LookupResources path, you'll also need to recover the original caveat context and submit it in the request.

### How do I prevent cycles when writing relationships?

Before writing a relationship that could create a cycle, use `CheckPermission` to verify the relationship won't create a loop.
Expand Down
39 changes: 20 additions & 19 deletions app/spicedb/modeling/validation-testing-debugging/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ You can run the integration test server by executing `spicedb serve-testing` or

[integration-action]: #authzedaction-spicedb

### CheckPermission Tracing Header
### Check Tracing

While it is recommended that SpiceDB schema be validated and tested before production deployment, there are many scenarios where being able to see the actual paths taken against production data is incredibly important.

To support this, SpiceDB's v1 CheckPermission API supports a debug header that will cause SpiceDB to trace the full set of relations and permission traversed while computing the check.
Sometimes you may need to see exactly which relationship paths led to a result in a CheckPermission query. To this end, the [CheckPermission](https://buf.build/authzed/api/docs/main%3Aauthzed.api.v1#authzed.api.v1.CheckPermissionRequest) API supports adding a boolean `withTracing` request parameter. This will cause SpiceDB to trace the full set of relations and permission traversed while computing the check.

<Callout type="warning">
**Warning:**
Expand All @@ -38,9 +36,12 @@ Instead, we recommend using [zed's explain flag] for this purpose.

</Callout>

Configuring this header is done by setting the header `io.spicedb.requestdebuginfo` to the string `true`.
The response will include a field, `debug_trace`, that contains the trace in proto form.

The response will include a trailer, `io.spicedb.respmeta.debuginfo`, with a JSON-encoded tree.
<Callout type="warning">
**Warning:** In versions older than v1.31.0, request tracing information via a header and the
information will be found in the response footer as JSON.
</Callout>

## Playground

Expand Down Expand Up @@ -148,17 +149,6 @@ project:docs#admin:
- "[user:rauchg[...]] is <platform:vercel#admin>/<platform:vercel#banned>"
```

## Check Tracing

SpiceDB supports tracing of check requests to view the path(s) taken to compute the result, as well as timing information.

Request tracing information by setting `with_tracing: true` in the request message and the information will be found in the response message.

<Callout type="warning">
**Warning:** In versions older than v1.31.0, request tracing information via a header and the
information will be found in the response footer as JSON.
</Callout>

## Zed
Comment thread
miparnisari marked this conversation as resolved.

### Zed Validate
Expand Down Expand Up @@ -228,7 +218,6 @@ If you're using GitHub, there's a [GitHub Action][validate-action] for running t
### Explain Flag

The `zed permission check` command has an optional flag, `--explain`, that will cause SpiceDB to collect the actual paths taken against the live system to compute a permission check.
If you're interested in learning more about this functionality in SpiceDB, you can read about the [tracing header] above.

Here's an example using `--explain`:

Expand All @@ -244,7 +233,19 @@ true
This command will also highlight which parts of the traversal were cached and if a [cycle] is detected.

[cycle]: ./recursion-and-max-depth#cyclical-relationships-cycles
[tracing header]: #checkpermission-tracing-header

### Debug Flag

The `zed permission lookup-resources` command has an optional flag, `--debug`, that will cause SpiceDB to put extra debugging information into the error details when a Max Recursion Depth error is hit. You can use this to help diagnose [cycle]s in your `LookupResources` calls that can't be found directly with `check --explain`.

Here's an example using `--debug`:

```
$ zed permission lookup-resources --debug document view user:fred
The following resource/relation pairs were found in a cycle in LookupResources:

- folder:a#view
```

## SpiceDB GitHub Actions

Expand Down
Loading