@@ -27,8 +27,9 @@ var _ datastore.ReadWriteTransaction = (*ydbReadWriter)(nil)
27
27
28
28
type ydbReadWriter struct {
29
29
* ydbReader
30
- bulkLoadBatchSize int
31
- newRevision revisions.TimestampRevision
30
+ bulkLoadBatchSize int
31
+ newRevision revisions.TimestampRevision
32
+ enableUniquenessCheck bool
32
33
}
33
34
34
35
// WriteCaveats stores the provided caveats.
@@ -62,11 +63,9 @@ func (rw *ydbReadWriter) DeleteCaveats(ctx context.Context, names []string) erro
62
63
63
64
// WriteRelationships takes a list of tuple mutations and applies them to the datastore.
64
65
func (rw * ydbReadWriter ) WriteRelationships (ctx context.Context , mutations []* core.RelationTupleUpdate ) error {
65
- insertBuilder := insertRelationsBuilder
66
66
deleteBuilder := deleteRelationBuilder
67
67
68
68
var (
69
- err error
70
69
insertionTuples []* core.RelationTuple
71
70
touchTuples []* core.RelationTuple
72
71
@@ -80,10 +79,6 @@ func (rw *ydbReadWriter) WriteRelationships(ctx context.Context, mutations []*co
80
79
switch mut .GetOperation () {
81
80
case core .RelationTupleUpdate_CREATE :
82
81
insertionTuples = append (insertionTuples , tpl )
83
- insertBuilder , err = appendForInsertion (insertBuilder , tpl , rw .newRevision )
84
- if err != nil {
85
- return fmt .Errorf ("failed to append tuple for insertion: %w" , err )
86
- }
87
82
88
83
case core .RelationTupleUpdate_TOUCH :
89
84
touchTuples = append (touchTuples , tpl )
@@ -97,7 +92,7 @@ func (rw *ydbReadWriter) WriteRelationships(ctx context.Context, mutations []*co
97
92
}
98
93
99
94
// Perform SELECT queries first as a part of uniqueness check.
100
- if len (insertionTuples ) > 0 {
95
+ if len (insertionTuples ) > 0 && rw . enableUniquenessCheck {
101
96
dups , err := rw .selectTuples (ctx , insertionTuples )
102
97
if err != nil {
103
98
return fmt .Errorf ("failed to ensure CREATE tuples uniqueness: %w" , err )
@@ -121,10 +116,6 @@ func (rw *ydbReadWriter) WriteRelationships(ctx context.Context, mutations []*co
121
116
pk := tuple .StringWithoutCaveat (touchTuples [i ])
122
117
dup , ok := duplicatePKToRel [pk ]
123
118
if ! ok {
124
- insertBuilder , err = appendForInsertion (insertBuilder , touchTuples [i ], rw .newRevision )
125
- if err != nil {
126
- return fmt .Errorf ("failed to append tuple for insertion: %w" , err )
127
- }
128
119
i ++
129
120
continue
130
121
}
@@ -136,10 +127,6 @@ func (rw *ydbReadWriter) WriteRelationships(ctx context.Context, mutations []*co
136
127
}
137
128
138
129
deleteClauses = append (deleteClauses , exactRelationshipClause (dup ))
139
- insertBuilder , err = appendForInsertion (insertBuilder , touchTuples [i ], rw .newRevision )
140
- if err != nil {
141
- return fmt .Errorf ("failed to append tuple for insertion: %w" , err )
142
- }
143
130
i ++
144
131
}
145
132
}
@@ -160,9 +147,18 @@ func (rw *ydbReadWriter) WriteRelationships(ctx context.Context, mutations []*co
160
147
161
148
// Run CREATE and TOUCH insertions, if any.
162
149
if len (insertionTuples ) > 0 || len (touchTuples ) > 0 {
163
- if err := executeQuery (ctx , rw .tablePathPrefix , rw .executor , insertBuilder ); err != nil {
150
+ in := append (insertionTuples , touchTuples ... )
151
+ query := ydbCommon .AddTablePrefix (insertAsTableRelationsYQL , rw .tablePathPrefix )
152
+
153
+ res , err := rw .executor .Execute (
154
+ ctx ,
155
+ query ,
156
+ table .NewQueryParameters (table .ValueParam ("$values" , relationsToListValue (in , rw .newRevision ))),
157
+ )
158
+ if err != nil {
164
159
return fmt .Errorf ("failed to insert tuples: %w" , err )
165
160
}
161
+ defer res .Close ()
166
162
}
167
163
168
164
return nil
@@ -289,12 +285,14 @@ func (rw *ydbReadWriter) BulkLoad(ctx context.Context, iter datastore.BulkWriteR
289
285
}
290
286
291
287
if len (insertionTuples ) > 0 {
292
- dups , err := rw .selectTuples (ctx , insertionTuples )
293
- if err != nil {
294
- return 0 , fmt .Errorf ("failed to ensure CREATE tuples uniqueness: %w" , err )
295
- }
296
- if len (dups ) > 0 {
297
- return 0 , datastoreCommon .NewCreateRelationshipExistsError (dups [0 ])
288
+ if rw .enableUniquenessCheck {
289
+ dups , err := rw .selectTuples (ctx , insertionTuples )
290
+ if err != nil {
291
+ return 0 , fmt .Errorf ("failed to ensure CREATE tuples uniqueness: %w" , err )
292
+ }
293
+ if len (dups ) > 0 {
294
+ return 0 , datastoreCommon .NewCreateRelationshipExistsError (dups [0 ])
295
+ }
298
296
}
299
297
300
298
if err := executeQuery (ctx , rw .tablePathPrefix , rw .executor , insertBuilder ); err != nil {
@@ -311,28 +309,22 @@ func (rw *ydbReadWriter) BulkLoad(ctx context.Context, iter datastore.BulkWriteR
311
309
return uint64 (totalCount ), nil
312
310
}
313
311
314
- func (rw * ydbReadWriter ) selectTuples (
315
- ctx context.Context ,
316
- in []* core.RelationTuple ,
317
- ) ([]* core.RelationTuple , error ) {
312
+ func (rw * ydbReadWriter ) selectTuples (ctx context.Context , in []* core.RelationTuple ) ([]* core.RelationTuple , error ) {
318
313
ctx , span := tracer .Start (ctx , "selectTuples" , trace .WithAttributes (attribute .Int ("count" , len (in ))))
319
314
defer span .End ()
320
315
321
316
if len (in ) == 0 {
322
317
return nil , nil
323
318
}
324
319
325
- var pred sq.Or
326
- for _ , r := range in {
327
- pred = append (pred , exactRelationshipClause (r ))
328
- }
329
-
330
- sql , args , err := rw .modifier (readRelationBuilder ).View (ixUqRelationLiving ).Where (pred ).ToYQL ()
331
- if err != nil {
332
- return nil , fmt .Errorf ("failed to build query: %w" , err )
333
- }
334
-
335
- return queryTuples (ctx , rw .tablePathPrefix , sql , table .NewQueryParameters (args ... ), span , rw .executor )
320
+ return queryTuples (
321
+ ctx ,
322
+ rw .tablePathPrefix ,
323
+ readLivingRelationYQL ,
324
+ table .NewQueryParameters (table .ValueParam ("$values" , relationsToSelectListValueParam (in ))),
325
+ span ,
326
+ rw .executor ,
327
+ )
336
328
}
337
329
338
330
type coreDefinition interface {
@@ -441,6 +433,50 @@ func appendForInsertion(
441
433
return b .Values (valuesToWrite ... ), nil
442
434
}
443
435
436
+ func relationsToListValue (in []* core.RelationTuple , rev revisions.TimestampRevision ) types.Value {
437
+ out := make ([]types.Value , len (in ))
438
+ for i := range in {
439
+ var caveatName * string
440
+ var caveatContext * []byte
441
+ if in [i ].GetCaveat () != nil {
442
+ caveatName = & in [i ].GetCaveat ().CaveatName
443
+ cc , err := in [i ].GetCaveat ().GetContext ().MarshalJSON ()
444
+ if err != nil {
445
+ panic (err )
446
+ }
447
+ caveatContext = & cc
448
+ }
449
+
450
+ out [i ] = types .StructValue (
451
+ types .StructFieldValue (colNamespace , types .UTF8Value (in [i ].GetResourceAndRelation ().GetNamespace ())),
452
+ types .StructFieldValue (colObjectID , types .UTF8Value (in [i ].GetResourceAndRelation ().GetObjectId ())),
453
+ types .StructFieldValue (colRelation , types .UTF8Value (in [i ].GetResourceAndRelation ().GetRelation ())),
454
+ types .StructFieldValue (colUsersetNamespace , types .UTF8Value (in [i ].GetSubject ().GetNamespace ())),
455
+ types .StructFieldValue (colUsersetObjectID , types .UTF8Value (in [i ].GetSubject ().GetObjectId ())),
456
+ types .StructFieldValue (colUsersetRelation , types .UTF8Value (in [i ].GetSubject ().GetRelation ())),
457
+ types .StructFieldValue (colCaveatName , types .NullableUTF8Value (caveatName )),
458
+ types .StructFieldValue (colCaveatContext , types .NullableJSONDocumentValueFromBytes (caveatContext )),
459
+ types .StructFieldValue (colCreatedAtUnixNano , types .Int64Value (rev .TimestampNanoSec ())),
460
+ )
461
+ }
462
+ return types .ListValue (out ... )
463
+ }
464
+
465
+ func relationsToSelectListValueParam (in []* core.RelationTuple ) types.Value {
466
+ out := make ([]types.Value , len (in ))
467
+ for i := range in {
468
+ out [i ] = types .TupleValue (
469
+ types .UTF8Value (in [i ].GetResourceAndRelation ().GetNamespace ()),
470
+ types .UTF8Value (in [i ].GetResourceAndRelation ().GetObjectId ()),
471
+ types .UTF8Value (in [i ].GetResourceAndRelation ().GetRelation ()),
472
+ types .UTF8Value (in [i ].GetSubject ().GetNamespace ()),
473
+ types .UTF8Value (in [i ].GetSubject ().GetObjectId ()),
474
+ types .UTF8Value (in [i ].GetSubject ().GetRelation ()),
475
+ )
476
+ }
477
+ return types .ListValue (out ... )
478
+ }
479
+
444
480
func exactRelationshipClause (r * core.RelationTuple ) sq.Eq {
445
481
return sq.Eq {
446
482
colNamespace : r .GetResourceAndRelation ().GetNamespace (),
0 commit comments