Skip to content

Commit

Permalink
document expiring relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
vroldanbet committed Jan 21, 2025
1 parent c8cd78e commit 8e26a96
Showing 1 changed file with 142 additions and 0 deletions.
142 changes: 142 additions & 0 deletions pages/spicedb/concepts/expiring-relationships.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { Callout } from 'nextra/components';
import YouTube from 'react-youtube';

# Expiring Relationships

<Callout type="info">
Expiring Relationships is available from SpiceDB 1.40 onwards.
</Callout>

<Callout type="info">
The clock used to determine if a relationship is expired is that of the underlying SpiceDB datastore.
This gets trickier when using distributed databases like CockroachDB or Spanner, where clocks have an uncertainty range.
When operating your own database, it's key to keep node clocks in sync - we recommend services like [Amazon Time Sync Service](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html).
You should evaluate the impact of clock drift in your application.
</Callout>

A common use case is to model relationships that expire after a certain time.
This is useful for granting temporary access to a resource.

Until now, caveats were the recommended way to support time-bound permissions, but that has some limitations:

- It requires clients to provide the `now` timestamp.
This is additional complexity for clients.
- Expired caveats are not automatically garbage collected.
This can lead to many caveated relationships in the system and increase the costs of loading and evaluating those into the runtime.

SpiceDB supports expiring relationships, which lets users define relationships that expire at a given time.

## Schema Use

Expiring relationships follow a similar use to caveated subject types.
The novelty here is that to disambiguate between a caveat named `expiration` and the native `expiration` feature,
users would need to add a `use` clause to the schema definition to enable the feature.

```zed
use expiration
definition folder {}
definition resource {
relation folder: folder with expiration
}
```

## API Use

The expiration of a relationship is [on a per-relationship basis](https://buf.build/authzed/api/docs/63b8911ef2871c56e5048d1f40a8473f98457ca9:authzed.api.v1#authzed.api.v1.Relationship)
at write time, using `WriteRelationships` or `BulkImportRelationships` APIs.
The expiration is part of the `OptionalExpiresAt` field in the relationship.

```textproto
WriteRelationshipsRequest {
Updates: [
RelationshipUpdate{
Operation: CREATE
Relationship: {
Resource: {
ObjectType: "resource",
ObjectId: "someresource",
},
Relation: "viewer",
Subject: {
ObjectType: "user",
ObjectId: "sarah",
},
OptionalExpiresAt: "2022-12-31T23:59:59Z"
}
}
]
}
```

## Garbage Collection

Reclaiming expiring relationships is governed by the same mechanism (and flags) as the deletion of the history of
relationship changes that powers SpiceDB's own MVCC (Multi-Version Concurrency Control) and heavily depends on
the datastore chosen.

- Datastores like Spanner and CockroachDB have built-in support for expiring SQL rows, so the database does Garbage Collection.
In both cases, expired relationships will be reclaimed after 24 hours, which can't be changed without directly manipulating the SQL schema.
- Datastores like Postgres and MySQL support it using the same GC job that reclaims old relationship versions, which runs every 5 minutes.
Unlike Spanner and CockroachDB, you can govern the GC window with the corresponding flags.
Relationships will be reclaimed after 24 hours by default.

<Callout type="info">
The GC Window should be adjusted according to the application's needs. How far back in time does your application need to go?
If this is a common use case, we recommend drastically reducing the GC window (e.g., 1 hour or 30 minutes).
This means SpiceDB will have to evaluate less data when serving authorization checks, which can improve performance
drastically in large-scale deployments.
</Callout>

## Migrating Off Expiration With Caveats

If you implemented expiration using caveats, this section describes migrating to the new expiration feature.

1. Rename your caveat if you had named it `expiration`
2. Add the new subject type to your relation, and also add a combination where both are used:

```zed
caveat ttl(timeout duration, now string, timeout_creation_timestamp string) {
timestamp(now) - timestamp(timeout_creation_timestamp) < timeout
}
definition folder {}
definition resource {
relation folder: folder with ttl
}
```

Becomes:

```zed
use expiration
caveat ttl(timeout duration, now string, timeout_creation_timestamp string) {
timestamp(now) - timestamp(timeout_creation_timestamp) < timeout
}
definition folder {}
definition resource {
relation folder: folder with ttl | folder with expiration | folder with ttl and expiration
}
```

3. Migrate all relationships to use both the caveat and the new expiration.
This is needed because only one relationship is allowed for a resource/permission/subject combination.
4. Validate that the new expiration feature works as expected by not providing the context for evaluating the `ttl` caveat.
5. Once validated, migrate completely to the new expiration feature by writing all relationships with only expiration
and without caveat.
6. Drop the caveat from your schema once the migration is completed

```zed
use expiration
definition folder {}
definition resource {
relation folder: folder with expiration
}
```

0 comments on commit 8e26a96

Please sign in to comment.