github.com/quay/claircore@v1.5.28/datastore/postgres/indexdistributions.go (about)

     1  package postgres
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/prometheus/client_golang/prometheus"
     9  	"github.com/prometheus/client_golang/prometheus/promauto"
    10  	"github.com/quay/claircore"
    11  	"github.com/quay/claircore/indexer"
    12  	"github.com/quay/claircore/pkg/microbatch"
    13  )
    14  
    15  var (
    16  	indexDistributionsCounter = promauto.NewCounterVec(
    17  		prometheus.CounterOpts{
    18  			Namespace: "claircore",
    19  			Subsystem: "indexer",
    20  			Name:      "indexdistributions_total",
    21  			Help:      "Total number of database queries issued in the IndexDistributions method.",
    22  		},
    23  		[]string{"query"},
    24  	)
    25  
    26  	indexDistributionsDuration = promauto.NewHistogramVec(
    27  		prometheus.HistogramOpts{
    28  			Namespace: "claircore",
    29  			Subsystem: "indexer",
    30  			Name:      "indexdistributions_duration_seconds",
    31  			Help:      "The duration of all queries issued in the IndexDistributions method",
    32  		},
    33  		[]string{"query"},
    34  	)
    35  )
    36  
    37  func (s *IndexerStore) IndexDistributions(ctx context.Context, dists []*claircore.Distribution, layer *claircore.Layer, scnr indexer.VersionedScanner) error {
    38  	const (
    39  		insert = `
    40  		INSERT INTO dist 
    41  			(name, did, version, version_code_name, version_id, arch, cpe, pretty_name) 
    42  		VALUES 
    43  			($1, $2, $3, $4, $5, $6, $7, $8) 
    44  		ON CONFLICT (name, did, version, version_code_name, version_id, arch, cpe, pretty_name) DO NOTHING;
    45  		`
    46  
    47  		insertWith = `
    48  		WITH distributions AS (
    49  			SELECT id AS dist_id
    50  			FROM dist
    51  			WHERE name = $1
    52  			  AND did = $2
    53  			  AND version = $3
    54  			  AND version_code_name = $4
    55  			  AND version_id = $5
    56  			  AND arch = $6
    57  			  AND cpe = $7
    58  			  AND pretty_name = $8
    59  		),
    60  			 scanner AS (
    61  				 SELECT id AS scanner_id
    62  				 FROM scanner
    63  				 WHERE name = $9
    64  				   AND version = $10
    65  				   AND kind = $11
    66  			 ),
    67  			 layer AS (
    68  				 SELECT id AS layer_id
    69  				 FROM layer
    70  				 WHERE layer.hash = $12
    71  			 )
    72  		INSERT
    73  		INTO dist_scanartifact (layer_id, dist_id, scanner_id)
    74  		VALUES ((SELECT layer_id FROM layer),
    75  				(SELECT dist_id FROM distributions),
    76  				(SELECT scanner_id FROM scanner))
    77  		ON CONFLICT DO NOTHING;
    78  		`
    79  	)
    80  
    81  	// obtain a transaction scoped batch
    82  	tx, err := s.pool.Begin(ctx)
    83  	if err != nil {
    84  		return fmt.Errorf("store:indexDistributions failed to create transaction: %v", err)
    85  	}
    86  	defer tx.Rollback(ctx)
    87  
    88  	insertDistStmt, err := tx.Prepare(ctx, "insertDistStmt", insert)
    89  	if err != nil {
    90  		return fmt.Errorf("failed to create statement: %w", err)
    91  	}
    92  	insertDistScanArtifactWithStmt, err := tx.Prepare(ctx, "insertDistScanArtifactWith", insertWith)
    93  	if err != nil {
    94  		return fmt.Errorf("failed to create statement: %w", err)
    95  	}
    96  
    97  	start := time.Now()
    98  	mBatcher := microbatch.NewInsert(tx, 500, time.Minute)
    99  	for _, dist := range dists {
   100  		err := mBatcher.Queue(
   101  			ctx,
   102  			insertDistStmt.SQL,
   103  			dist.Name,
   104  			dist.DID,
   105  			dist.Version,
   106  			dist.VersionCodeName,
   107  			dist.VersionID,
   108  			dist.Arch,
   109  			dist.CPE,
   110  			dist.PrettyName,
   111  		)
   112  		if err != nil {
   113  			return fmt.Errorf("batch insert failed for dist %v: %w", dist, err)
   114  		}
   115  	}
   116  	err = mBatcher.Done(ctx)
   117  	if err != nil {
   118  		return fmt.Errorf("final batch insert failed for dist: %w", err)
   119  	}
   120  	indexDistributionsCounter.WithLabelValues("insert_batch").Add(1)
   121  	indexDistributionsDuration.WithLabelValues("insert_batch").Observe(time.Since(start).Seconds())
   122  
   123  	// make dist scan artifacts
   124  	start = time.Now()
   125  	mBatcher = microbatch.NewInsert(tx, 500, time.Minute)
   126  	for _, dist := range dists {
   127  		err := mBatcher.Queue(
   128  			ctx,
   129  			insertDistScanArtifactWithStmt.SQL,
   130  			dist.Name,
   131  			dist.DID,
   132  			dist.Version,
   133  			dist.VersionCodeName,
   134  			dist.VersionID,
   135  			dist.Arch,
   136  			dist.CPE,
   137  			dist.PrettyName,
   138  			scnr.Name(),
   139  			scnr.Version(),
   140  			scnr.Kind(),
   141  			layer.Hash,
   142  		)
   143  		if err != nil {
   144  			return fmt.Errorf("batch insert failed for dist_scanartifact %v: %w", dist, err)
   145  		}
   146  	}
   147  	err = mBatcher.Done(ctx)
   148  	if err != nil {
   149  		return fmt.Errorf("final batch insert failed for dist_scanartifact: %w", err)
   150  	}
   151  	indexDistributionsCounter.WithLabelValues("insertWith_batch").Add(1)
   152  	indexDistributionsDuration.WithLabelValues("insertWith_batch").Observe(time.Since(start).Seconds())
   153  
   154  	err = tx.Commit(ctx)
   155  	if err != nil {
   156  		return fmt.Errorf("store:indexDistributions failed to commit tx: %w", err)
   157  	}
   158  	return nil
   159  }