@@ -126,11 +126,12 @@ func (db *psqlxStore) ProcessSourceExecution(s Source, sourceFindings []SourceFi
126
126
// conditions from other possible concurrent sources being processed.
127
127
// To do that we have to lock based on all related sources that can
128
128
// have an effect on the current source being processed and its findings.
129
- relatedFamilies , err := db .relatedFamilies (s .SourceFamily )
129
+ relatedComponents , err := db .relatedComponents (s .SourceFamily )
130
130
if err != nil {
131
131
return Source {}, err
132
132
}
133
- err = db .lockTxBySources (tx , relatedFamilies )
133
+ // err = db.lockTxBySources(tx, relatedFamilies)
134
+ err = db .lockTxByComponent (tx , s .Target , relatedComponents )
134
135
if err != nil {
135
136
return Source {}, err
136
137
}
@@ -166,7 +167,7 @@ func (db *psqlxStore) ProcessSourceExecution(s Source, sourceFindings []SourceFi
166
167
}
167
168
168
169
tFindings := time .Now ()
169
- findingStates , err := findingsStates (ctx , tx , db .logger , screated . SourceFamily )
170
+ findingStates , err := findingsStatesV2 (ctx , tx , db .logger , s . Target , relatedComponents )
170
171
if err != nil {
171
172
tx .Rollback ()
172
173
return Source {}, err
@@ -257,6 +258,31 @@ func (db *psqlxStore) relatedFamilies(sf SourceFamily) (SourceFamilies, error) {
257
258
return families , nil
258
259
}
259
260
261
+ // RelatedFamilies returns the related families for the input SourceFamily.
262
+ // That is the source families that are capable of detecting at least one
263
+ // of the issues that the input source has ever detected for the specified target.
264
+ func (db * psqlxStore ) relatedComponents (sf SourceFamily ) ([]string , error ) {
265
+ q := `
266
+ SELECT UNNEST(comps)
267
+ FROM (
268
+ SELECT DISTINCT ARRAY_AGG(DISTINCT s.component) comps
269
+ FROM sources s INNER JOIN source_issues si ON si.source_id = s.id
270
+ WHERE s.target_id = $3 AND s.name=$1
271
+ GROUP BY issue_id
272
+ HAVING $2 = ANY(ARRAY_AGG(DISTINCT s.component))
273
+ ) C
274
+ `
275
+
276
+ args := []interface {}{sf .Name , sf .Component , sf .Target }
277
+ logQuery (db .logger , "relatedFamilies" , q , args ... )
278
+ components := []string {}
279
+ err := db .DB .Select (& components , q , args ... )
280
+ if err != nil && ! IsNotFoundErr (err ) {
281
+ return nil , err
282
+ }
283
+ return components , nil
284
+ }
285
+
260
286
// lockTxBySources performs an exclusive advisory lock on the given
261
287
// transaction genereting a lock for each specified source family.
262
288
func (db * psqlxStore ) lockTxBySources (tx * sqlx.Tx , sff SourceFamilies ) error {
@@ -285,6 +311,33 @@ func (db *psqlxStore) lockTxBySources(tx *sqlx.Tx, sff SourceFamilies) error {
285
311
return nil
286
312
}
287
313
314
+ // lockTxBySources performs an exclusive advisory lock on the given
315
+ // transaction genereting a lock for each specified source family.
316
+ func (db * psqlxStore ) lockTxByComponent (tx * sqlx.Tx , target string , components []string ) error {
317
+ // We use advisory locks and transactions to avoid race conditions
318
+ // and inconsistent DB state, for instance, with a source without its
319
+ // finding events, that would lead to wrong findind exposures calculation.
320
+
321
+ for _ , sf := range components {
322
+ // A lock is obtained for every source family in scope.
323
+ // The key for the lock is built by concatenating the
324
+ // Name, Component and Target for each source family.
325
+ fkey := fmt .Sprintf ("%s%s" , sf , target )
326
+
327
+ h := fnv .New32 ()
328
+ h .Write ([]byte (fkey ))
329
+ k := h .Sum32 ()
330
+
331
+ _ , err := tx .Exec ("SELECT pg_advisory_xact_lock($1)" , k )
332
+ if err != nil {
333
+ tx .Rollback ()
334
+ return err
335
+ }
336
+ }
337
+
338
+ return nil
339
+ }
340
+
288
341
func createSource (ctx context.Context , tx * sqlx.Tx , s Source ) (* Source , error ) {
289
342
r := tx .QueryRowxContext (ctx , `INSERT INTO sources (name, component, instance, options, time, target_id)
290
343
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *` , s .Name , s .Component , s .Instance , s .Options , s .Time , s .Target )
0 commit comments