Skip to content

Commit

Permalink
feat: deduplicate UUID mappings before database insert (#1654)
Browse files Browse the repository at this point in the history
  • Loading branch information
alnr authored Dec 6, 2024
1 parent 1e7a146 commit ac812ee
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 11 deletions.
19 changes: 19 additions & 0 deletions internal/e2e/transaction_cases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@ func runTransactionCases(c transactClient, m *namespaceTestManager) func(*testin
assert.Len(t, resp.RelationTuples, 0)
})

t.Run("case=duplicate string representations", func(t *testing.T) {
n := &namespace.Namespace{Name: t.Name()}
m.add(t, n)
c.transactTuples(t, []*ketoapi.RelationTuple{
{
Namespace: n.Name,
Object: "o",
Relation: "rel",
SubjectID: pointerx.Ptr("sid"),
},
{
Namespace: n.Name,
Object: "o",
Relation: "rel",
SubjectID: pointerx.Ptr("sid"),
},
}, nil)
})

t.Run("case=large inserts and deletes", func(t *testing.T) {
if !testing.Short() {
t.Skip("This test is fairly expensive, especially the deletion.")
Expand Down
16 changes: 8 additions & 8 deletions internal/persistence/sql/relationtuples.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,14 @@ func buildDelete(nid uuid.UUID, rs []*relationtuple.RelationTuple) (query string
}

func (p *Persister) DeleteRelationTuples(ctx context.Context, rs ...*relationtuple.RelationTuple) (err error) {
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.DeleteRelationTuples",
trace.WithAttributes(attribute.Int("count", len(rs))))
defer otelx.End(span, &err)

if len(rs) == 0 {
return nil
}

ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.DeleteRelationTuples",
trace.WithAttributes(attribute.Int("count", len(rs))))
defer otelx.End(span, &err)

return p.Transaction(ctx, func(ctx context.Context) error {
for chunk := range slices.Chunk(rs, chunkSizeDeleteTuple) {
q, args, err := buildDelete(p.NetworkID(ctx), chunk)
Expand Down Expand Up @@ -310,14 +310,14 @@ func buildInsert(commitTime time.Time, nid uuid.UUID, rs []*relationtuple.Relati
}

func (p *Persister) WriteRelationTuples(ctx context.Context, rs ...*relationtuple.RelationTuple) (err error) {
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.WriteRelationTuples",
trace.WithAttributes(attribute.Int("count", len(rs))))
defer otelx.End(span, &err)

if len(rs) == 0 {
return nil
}

ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.WriteRelationTuples",
trace.WithAttributes(attribute.Int("count", len(rs))))
defer otelx.End(span, &err)

commitTime := time.Now()

return p.Transaction(ctx, func(ctx context.Context) error {
Expand Down
18 changes: 15 additions & 3 deletions internal/persistence/sql/uuid_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package sql

import (
"bytes"
"context"
"iter"
"maps"
Expand All @@ -13,6 +14,8 @@ import (
"github.com/gofrs/uuid"
"github.com/ory/x/otelx"
"github.com/ory/x/sqlcon"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"

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

func (p *Persister) MapStringsToUUIDs(ctx context.Context, values ...string) (uuids []uuid.UUID, err error) {
ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.MapStringsToUUIDs")
defer otelx.End(span, &err)

if len(values) == 0 {
return
}

ctx, span := p.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.MapStringsToUUIDs",
trace.WithAttributes(attribute.Int("num_values", len(values))))
defer otelx.End(span, &err)

uuids, err = p.MapStringsToUUIDsReadOnly(ctx, values...)
if err != nil {
return nil, err
Expand All @@ -110,6 +114,14 @@ func (p *Persister) MapStringsToUUIDs(ctx context.Context, values ...string) (uu
StringRepresentation: values[i],
}
}
slices.SortFunc(mappings, func(a, b UUIDMapping) int {
return bytes.Compare(a.ID[:], b.ID[:])
})
mappings = slices.CompactFunc(mappings, func(a, b UUIDMapping) bool {
return a.ID == b.ID
})

span.SetAttributes(attribute.Int("num_mappings", len(mappings)))

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

0 comments on commit ac812ee

Please sign in to comment.