Skip to content

Commit ac812ee

Browse files
authored
feat: deduplicate UUID mappings before database insert (#1654)
1 parent 1e7a146 commit ac812ee

File tree

3 files changed

+42
-11
lines changed

3 files changed

+42
-11
lines changed

internal/e2e/transaction_cases_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,25 @@ func runTransactionCases(c transactClient, m *namespaceTestManager) func(*testin
6262
assert.Len(t, resp.RelationTuples, 0)
6363
})
6464

65+
t.Run("case=duplicate string representations", func(t *testing.T) {
66+
n := &namespace.Namespace{Name: t.Name()}
67+
m.add(t, n)
68+
c.transactTuples(t, []*ketoapi.RelationTuple{
69+
{
70+
Namespace: n.Name,
71+
Object: "o",
72+
Relation: "rel",
73+
SubjectID: pointerx.Ptr("sid"),
74+
},
75+
{
76+
Namespace: n.Name,
77+
Object: "o",
78+
Relation: "rel",
79+
SubjectID: pointerx.Ptr("sid"),
80+
},
81+
}, nil)
82+
})
83+
6584
t.Run("case=large inserts and deletes", func(t *testing.T) {
6685
if !testing.Short() {
6786
t.Skip("This test is fairly expensive, especially the deletion.")

internal/persistence/sql/relationtuples.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,14 @@ func buildDelete(nid uuid.UUID, rs []*relationtuple.RelationTuple) (query string
175175
}
176176

177177
func (p *Persister) DeleteRelationTuples(ctx context.Context, rs ...*relationtuple.RelationTuple) (err error) {
178-
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.DeleteRelationTuples",
179-
trace.WithAttributes(attribute.Int("count", len(rs))))
180-
defer otelx.End(span, &err)
181-
182178
if len(rs) == 0 {
183179
return nil
184180
}
185181

182+
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.DeleteRelationTuples",
183+
trace.WithAttributes(attribute.Int("count", len(rs))))
184+
defer otelx.End(span, &err)
185+
186186
return p.Transaction(ctx, func(ctx context.Context) error {
187187
for chunk := range slices.Chunk(rs, chunkSizeDeleteTuple) {
188188
q, args, err := buildDelete(p.NetworkID(ctx), chunk)
@@ -310,14 +310,14 @@ func buildInsert(commitTime time.Time, nid uuid.UUID, rs []*relationtuple.Relati
310310
}
311311

312312
func (p *Persister) WriteRelationTuples(ctx context.Context, rs ...*relationtuple.RelationTuple) (err error) {
313-
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.WriteRelationTuples",
314-
trace.WithAttributes(attribute.Int("count", len(rs))))
315-
defer otelx.End(span, &err)
316-
317313
if len(rs) == 0 {
318314
return nil
319315
}
320316

317+
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.WriteRelationTuples",
318+
trace.WithAttributes(attribute.Int("count", len(rs))))
319+
defer otelx.End(span, &err)
320+
321321
commitTime := time.Now()
322322

323323
return p.Transaction(ctx, func(ctx context.Context) error {

internal/persistence/sql/uuid_mapping.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package sql
55

66
import (
7+
"bytes"
78
"context"
89
"iter"
910
"maps"
@@ -13,6 +14,8 @@ import (
1314
"github.com/gofrs/uuid"
1415
"github.com/ory/x/otelx"
1516
"github.com/ory/x/sqlcon"
17+
"go.opentelemetry.io/otel/attribute"
18+
"go.opentelemetry.io/otel/trace"
1619

1720
"github.com/ory/keto/internal/x"
1821
)
@@ -89,13 +92,14 @@ func (p *Persister) batchFromUUIDs(ctx context.Context, ids []uuid.UUID, opts ..
8992
}
9093

9194
func (p *Persister) MapStringsToUUIDs(ctx context.Context, values ...string) (uuids []uuid.UUID, err error) {
92-
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.MapStringsToUUIDs")
93-
defer otelx.End(span, &err)
94-
9595
if len(values) == 0 {
9696
return
9797
}
9898

99+
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.MapStringsToUUIDs",
100+
trace.WithAttributes(attribute.Int("num_values", len(values))))
101+
defer otelx.End(span, &err)
102+
99103
uuids, err = p.MapStringsToUUIDsReadOnly(ctx, values...)
100104
if err != nil {
101105
return nil, err
@@ -110,6 +114,14 @@ func (p *Persister) MapStringsToUUIDs(ctx context.Context, values ...string) (uu
110114
StringRepresentation: values[i],
111115
}
112116
}
117+
slices.SortFunc(mappings, func(a, b UUIDMapping) int {
118+
return bytes.Compare(a.ID[:], b.ID[:])
119+
})
120+
mappings = slices.CompactFunc(mappings, func(a, b UUIDMapping) bool {
121+
return a.ID == b.ID
122+
})
123+
124+
span.SetAttributes(attribute.Int("num_mappings", len(mappings)))
113125

114126
err = p.Transaction(ctx, func(ctx context.Context) error {
115127
for chunk := range slices.Chunk(mappings, chunkSizeInsertUUIDMappings) {

0 commit comments

Comments
 (0)