You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: guides/object_cache/caching.md
+70-1
Original file line number
Diff line number
Diff line change
@@ -73,7 +73,11 @@ Under the hood, `ttl:` is implemented with Redis's `EXPIRE`.
73
73
74
74
## Caching lists and connections
75
75
76
-
Lists and connections require a little extra consideration. In order to effectively bust the cache, items that belong to the list of "parent" object should __update the parent__ (eg, Rails `.touch`) whenever they're created, destroyed, or updated. For example, if there's a list of players on a team:
76
+
Lists and connections require a little extra consideration. By default, each _item_ in a list is registered with the cache, but when new items are created, they are unknown to the cache and therefore don't invalidate the cached result. There are two main approaches to address this.
77
+
78
+
### `has_many` lists
79
+
80
+
In order to effectively bust the cache, items that belong to the list of "parent" object should __update the parent__ (eg, Rails `.touch`) whenever they're created, destroyed, or updated. For example, if there's a list of players on a team:
77
81
78
82
```graphql
79
83
{
@@ -92,6 +96,71 @@ With Rails, you can accomplish this with:
92
96
belongs_to :team, touch:true
93
97
```
94
98
99
+
### Top-level lists
100
+
101
+
For `ActiveRecord::Relation`s _without_ a "parent" object, you can use `GraphQL::Enterprise::ObjectCache::CacheableRelation` to make a synthetic cache entry for the _whole_ relation. To use this class, make a subclass and implement `def items`, for example:
Then, in your resolver, use your new class to retrieve the items:
116
+
117
+
```ruby
118
+
classQuery < GraphQL::Schema::Object
119
+
field :teams, Team.connection_type do
120
+
argument :division, Division, required:false
121
+
end
122
+
123
+
defteams(division:nil)
124
+
AllTeams.items_for(self, division: division)
125
+
end
126
+
end
127
+
```
128
+
129
+
Finally, you'll need to handle `CacheableRelation`s in your object identification methods, for example:
130
+
131
+
```ruby
132
+
classMySchema < GraphQL::Schema
133
+
# ...
134
+
defself.id_from_object(object, type, ctx)
135
+
if object.is_a?(GraphQL::Enterprise::ObjectCache::CacheableRelation)
136
+
object.id
137
+
else
138
+
# The rest of your id_from_object logic here...
139
+
end
140
+
end
141
+
142
+
defself.object_from_id(id, ctx)
143
+
if (cacheable_rel =GraphQL::Enterprise::ObjectCache::CacheableRelation.find?(id))
144
+
cacheable_rel
145
+
else
146
+
# The rest of your object_from_id logic here...
147
+
end
148
+
end
149
+
end
150
+
```
151
+
152
+
In this example, `AllTeams` takes care of integrating with the cache:
153
+
154
+
- It implements `#id` to create a cache-friendly, stable global ID
155
+
- It implements `#to_param` to create a cache fingerprint (using Rails's `#cache_key` under the hood)
156
+
- It implements `.find?` to retrieve the list based on its ID
157
+
158
+
This way, if a `Team` is created, the cached result will be invalidated and a fresh result will be created.
159
+
160
+
Alternatively (or additionally), you could use a `ttl:` to expire cached results after a certain duration, just to be sure that results are eventually expired.
161
+
162
+
### Connections
163
+
95
164
By default, connection-related objects (like `*Connection` and `*Edge` types) "inherit" cacheability from their node types. You can override this in your base classes as long as `GraphQL::Enterprise::ObjectCache::ObjectIntegration` is included in the inheritance chain somewhere.
0 commit comments