github.com/quay/claircore@v1.5.28/datastore/postgres/indexpackage.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/zlog" 11 12 "github.com/quay/claircore" 13 "github.com/quay/claircore/indexer" 14 "github.com/quay/claircore/pkg/microbatch" 15 ) 16 17 var zeroPackage = claircore.Package{} 18 19 var ( 20 indexPackageCounter = promauto.NewCounterVec( 21 prometheus.CounterOpts{ 22 Namespace: "claircore", 23 Subsystem: "indexer", 24 Name: "indexpackage_total", 25 Help: "Total number of database queries issued in the IndexPackage method.", 26 }, 27 []string{"query"}, 28 ) 29 30 indexPackageDuration = promauto.NewHistogramVec( 31 prometheus.HistogramOpts{ 32 Namespace: "claircore", 33 Subsystem: "indexer", 34 Name: "indexpackage_duration_seconds", 35 Help: "The duration of all queries issued in the IndexPackage method", 36 }, 37 []string{"query"}, 38 ) 39 ) 40 41 // IndexPackages indexes all provided packages along with creating a scan artifact. 42 // 43 // If a source package is nested inside a binary package we index the source 44 // package first and then create a relation between the binary package and 45 // source package. 46 // 47 // Scan artifacts are used to determine if a particular layer has been scanned by a 48 // particular scanner. See the LayerScanned method for more details. 49 func (s *IndexerStore) IndexPackages(ctx context.Context, pkgs []*claircore.Package, layer *claircore.Layer, scnr indexer.VersionedScanner) error { 50 const ( 51 insert = ` 52 INSERT INTO package (name, kind, version, norm_kind, norm_version, module, arch) 53 VALUES ($1, $2, $3, $4, $5::int[], $6, $7) 54 ON CONFLICT (name, kind, version, module, arch) DO NOTHING; 55 ` 56 57 insertWith = ` 58 WITH source_package AS ( 59 SELECT id AS source_id 60 FROM package 61 WHERE name = $1 62 AND kind = $2 63 AND version = $3 64 AND module = $4 65 AND arch = $5 66 ), 67 binary_package AS ( 68 SELECT id AS package_id 69 FROM package 70 WHERE name = $6 71 AND kind = $7 72 AND version = $8 73 AND module = $9 74 AND arch = $10 75 ), 76 scanner AS ( 77 SELECT id AS scanner_id 78 FROM scanner 79 WHERE name = $11 80 AND version = $12 81 AND kind = $13 82 ), 83 layer AS ( 84 SELECT id AS layer_id 85 FROM layer 86 WHERE layer.hash = $14 87 ) 88 INSERT 89 INTO package_scanartifact (layer_id, package_db, repository_hint, filepath, package_id, source_id, scanner_id) 90 VALUES ((SELECT layer_id FROM layer), 91 $15, 92 $16, 93 $17, 94 (SELECT package_id FROM binary_package), 95 (SELECT source_id FROM source_package), 96 (SELECT scanner_id FROM scanner)) 97 ON CONFLICT DO NOTHING; 98 ` 99 ) 100 101 ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/indexPackages") 102 // obtain a transaction scoped batch 103 tx, err := s.pool.Begin(ctx) 104 if err != nil { 105 return fmt.Errorf("store:indexPackage failed to create transaction: %w", err) 106 } 107 defer tx.Rollback(ctx) 108 109 insertPackageStmt, err := tx.Prepare(ctx, "insertPackageStmt", insert) 110 if err != nil { 111 return fmt.Errorf("failed to create statement: %w", err) 112 } 113 insertPackageScanArtifactWithStmt, err := tx.Prepare(ctx, "insertPackageScanArtifactWith", insertWith) 114 if err != nil { 115 return fmt.Errorf("failed to create statement: %w", err) 116 } 117 118 skipCt := 0 119 120 start := time.Now() 121 mBatcher := microbatch.NewInsert(tx, 500, time.Minute) 122 for _, pkg := range pkgs { 123 if pkg.Name == "" { 124 skipCt++ 125 } 126 if pkg.Source == nil { 127 pkg.Source = &zeroPackage 128 } 129 130 if err := queueInsert(ctx, mBatcher, insertPackageStmt.Name, pkg.Source); err != nil { 131 return err 132 } 133 if err := queueInsert(ctx, mBatcher, insertPackageStmt.Name, pkg); err != nil { 134 return err 135 } 136 } 137 err = mBatcher.Done(ctx) 138 if err != nil { 139 return fmt.Errorf("final batch insert failed for pkg: %w", err) 140 } 141 indexPackageCounter.WithLabelValues("insert_batch").Add(1) 142 indexPackageDuration.WithLabelValues("insert_batch").Observe(time.Since(start).Seconds()) 143 144 zlog.Debug(ctx). 145 Int("skipped", skipCt). 146 Int("inserted", len(pkgs)-skipCt). 147 Msg("packages inserted") 148 149 skipCt = 0 150 // make package scan artifacts 151 mBatcher = microbatch.NewInsert(tx, 500, time.Minute) 152 153 start = time.Now() 154 for _, pkg := range pkgs { 155 if pkg.Name == "" { 156 skipCt++ 157 continue 158 } 159 err := mBatcher.Queue( 160 ctx, 161 insertPackageScanArtifactWithStmt.SQL, 162 pkg.Source.Name, 163 pkg.Source.Kind, 164 pkg.Source.Version, 165 pkg.Source.Module, 166 pkg.Source.Arch, 167 pkg.Name, 168 pkg.Kind, 169 pkg.Version, 170 pkg.Module, 171 pkg.Arch, 172 scnr.Name(), 173 scnr.Version(), 174 scnr.Kind(), 175 layer.Hash, 176 pkg.PackageDB, 177 pkg.RepositoryHint, 178 pkg.Filepath, 179 ) 180 if err != nil { 181 return fmt.Errorf("batch insert failed for package_scanartifact %v: %w", pkg, err) 182 } 183 } 184 err = mBatcher.Done(ctx) 185 if err != nil { 186 return fmt.Errorf("final batch insert failed for package_scanartifact: %w", err) 187 } 188 indexPackageCounter.WithLabelValues("insertWith_batch").Add(1) 189 indexPackageDuration.WithLabelValues("insertWith_batch").Observe(time.Since(start).Seconds()) 190 zlog.Debug(ctx). 191 Int("skipped", skipCt). 192 Int("inserted", len(pkgs)-skipCt). 193 Msg("scanartifacts inserted") 194 195 err = tx.Commit(ctx) 196 if err != nil { 197 return fmt.Errorf("store:indexPackages failed to commit tx: %w", err) 198 } 199 return nil 200 } 201 202 func queueInsert(ctx context.Context, b *microbatch.Insert, stmt string, pkg *claircore.Package) error { 203 var vKind *string 204 var vNorm []int32 205 if pkg.NormalizedVersion.Kind != "" { 206 vKind = &pkg.NormalizedVersion.Kind 207 vNorm = pkg.NormalizedVersion.V[:] 208 } 209 err := b.Queue(ctx, stmt, 210 pkg.Name, pkg.Kind, pkg.Version, vKind, vNorm, pkg.Module, pkg.Arch, 211 ) 212 if err != nil { 213 return fmt.Errorf("failed to queue insert for package %q: %w", pkg.Name, err) 214 } 215 return nil 216 }