Skip to content

Commit c8eb9db

Browse files
authored
Merge pull request #2326 from josephschorr/spanner-index-force
Force indexes on Spanner and change subject sort order to match index
2 parents 9fb9740 + a46b662 commit c8eb9db

File tree

4 files changed

+71
-1
lines changed

4 files changed

+71
-1
lines changed

internal/datastore/spanner/indexes.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package spanner
2+
3+
import (
4+
"github.com/authzed/spicedb/internal/datastore/common"
5+
"github.com/authzed/spicedb/pkg/datastore/queryshape"
6+
)
7+
8+
var IndexRelationshipBySubject = common.IndexDefinition{
9+
Name: "ix_relation_tuple_by_subject",
10+
ColumnsSQL: `relation_tuple (userset_object_id, userset_namespace, userset_relation, namespace, relation)`,
11+
}
12+
13+
var NoIndexingHint common.IndexingHint = nil
14+
15+
// IndexingHintForQueryShape returns an indexing hint for the given query shape, if any.
16+
func IndexingHintForQueryShape(schema common.SchemaInformation, qs queryshape.Shape) common.IndexingHint {
17+
if schema.IntegrityEnabled {
18+
// Don't force anything since we don't have the other indexes.
19+
return NoIndexingHint
20+
}
21+
22+
switch qs {
23+
case queryshape.MatchingResourcesForSubject:
24+
return forcedIndex{IndexRelationshipBySubject}
25+
26+
case queryshape.FindSubjectOfType:
27+
return forcedIndex{IndexRelationshipBySubject}
28+
29+
default:
30+
return NoIndexingHint
31+
}
32+
}
33+
34+
// forcedIndex is an index hint that forces the use of a specific index.
35+
type forcedIndex struct {
36+
index common.IndexDefinition
37+
}
38+
39+
func (f forcedIndex) FromSQLSuffix() (string, error) {
40+
return "", nil
41+
}
42+
43+
func (f forcedIndex) FromTable(existingTableName string) (string, error) {
44+
return existingTableName + "@{FORCE_INDEX=" + f.index.Name + "}", nil
45+
}
46+
47+
func (f forcedIndex) SQLPrefix() (string, error) {
48+
return "", nil
49+
}
50+
51+
var _ common.IndexingHint = forcedIndex{}

internal/datastore/spanner/reader.go

+7
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ func (sr spannerReader) QueryRelationships(
140140
return nil, err
141141
}
142142

143+
builtOpts := options.NewQueryOptionsWithOptions(opts...)
144+
indexingHint := IndexingHintForQueryShape(sr.schema, builtOpts.QueryShape)
145+
qBuilder = qBuilder.WithIndexingHint(indexingHint)
146+
143147
return sr.executor.ExecuteQuery(ctx, qBuilder, opts...)
144148
}
145149

@@ -162,6 +166,9 @@ func (sr spannerReader) ReverseQueryRelationships(
162166
FilterToRelation(queryOpts.ResRelation.Relation)
163167
}
164168

169+
indexingHint := IndexingHintForQueryShape(sr.schema, queryOpts.QueryShapeForReverse)
170+
qBuilder = qBuilder.WithIndexingHint(indexingHint)
171+
165172
return sr.executor.ExecuteQuery(ctx,
166173
qBuilder,
167174
options.WithLimit(queryOpts.LimitForReverse),

internal/datastore/spanner/spanner.go

+12
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,18 @@ func NewSpannerDatastore(ctx context.Context, database string, opts ...Option) (
207207
common.WithPlaceholderFormat(sq.AtP),
208208
common.WithNowFunction("CURRENT_TIMESTAMP"),
209209
common.WithColumnOptimization(config.columnOptimizationOption),
210+
211+
// NOTE: this order differs from the default because the index
212+
// used for sorting by subject (ix_relation_tuple_by_subject) is
213+
// defined with the userset object ID first.
214+
common.SetSortBySubjectColumnOrder([]string{
215+
colUsersetObjectID,
216+
colUsersetNamespace,
217+
colUsersetRelation,
218+
colNamespace,
219+
colRelation,
220+
colObjectID,
221+
}),
210222
)
211223

212224
ds := &spannerDatastore{

pkg/datastore/queryshape/queryshape.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const (
7373
// The query shape selects a subject of a given type, which is specified by
7474
// providing the subject type. The other fields are never specified.
7575
//
76-
// resource_type, *️⃣ resource_id, *️⃣ resource_relation, ✅ subject_type, *️⃣ subject_id, *️⃣ subject_relation, *️⃣ caveat, *️⃣ expiration
76+
// *️⃣ resource_type, *️⃣ resource_id, *️⃣ resource_relation, ✅ subject_type, *️⃣ subject_id, *️⃣ subject_relation, *️⃣ caveat, *️⃣ expiration
7777
FindSubjectOfType = "find-subject-of-type"
7878

7979
// FindResourceOfTypeAndRelation indicates that the query is selecting a single

0 commit comments

Comments
 (0)