github.com/quay/claircore@v1.5.28/datastore/postgres/indexmanifest.go (about) 1 package postgres 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "time" 8 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/prometheus/client_golang/prometheus/promauto" 11 "github.com/quay/zlog" 12 13 "github.com/quay/claircore" 14 "github.com/quay/claircore/pkg/microbatch" 15 ) 16 17 var ( 18 indexManifestCounter = promauto.NewCounterVec( 19 prometheus.CounterOpts{ 20 Namespace: "claircore", 21 Subsystem: "indexer", 22 Name: "indexmanifest_total", 23 Help: "Total number of database queries issued in the IndexManifest method.", 24 }, 25 []string{"query"}, 26 ) 27 28 indexManifestDuration = promauto.NewHistogramVec( 29 prometheus.HistogramOpts{ 30 Namespace: "claircore", 31 Subsystem: "indexer", 32 Name: "indexmanifest_duration_seconds", 33 Help: "The duration of all queries issued in the IndexManifest method", 34 }, 35 []string{"query"}, 36 ) 37 ) 38 39 func (s *IndexerStore) IndexManifest(ctx context.Context, ir *claircore.IndexReport) error { 40 const ( 41 query = ` 42 WITH manifests AS ( 43 SELECT id AS manifest_id 44 FROM manifest 45 WHERE hash = $4 46 ) 47 INSERT 48 INTO manifest_index(package_id, dist_id, repo_id, manifest_id) 49 VALUES ($1, $2, $3, (SELECT manifest_id FROM manifests)) 50 ON CONFLICT DO NOTHING; 51 ` 52 ) 53 ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/indexManifest") 54 55 if ir.Hash.String() == "" { 56 return fmt.Errorf("received empty hash. cannot associate contents with a manifest hash") 57 } 58 hash := ir.Hash.String() 59 60 records := ir.IndexRecords() 61 if len(records) == 0 { 62 zlog.Warn(ctx).Msg("manifest being indexed has 0 index records") 63 return nil 64 } 65 66 // obtain a transaction scoped batch 67 tx, err := s.pool.Begin(ctx) 68 if err != nil { 69 return fmt.Errorf("postgres: indexManifest failed to create transaction: %w", err) 70 } 71 defer tx.Rollback(ctx) 72 73 queryStmt, err := tx.Prepare(ctx, "queryStmt", query) 74 if err != nil { 75 return fmt.Errorf("failed to create statement: %w", err) 76 } 77 78 start := time.Now() 79 mBatcher := microbatch.NewInsert(tx, 500, time.Minute) 80 for _, record := range records { 81 // ignore nil packages 82 if record.Package == nil { 83 continue 84 } 85 86 v, err := toValues(*record) 87 if err != nil { 88 return fmt.Errorf("received a record with an invalid id: %v", err) 89 } 90 91 // if source package exists create record 92 if v[0] != nil { 93 err = mBatcher.Queue( 94 ctx, 95 queryStmt.SQL, 96 v[0], 97 v[2], 98 v[3], 99 hash, 100 ) 101 if err != nil { 102 return fmt.Errorf("batch insert failed for source package record %v: %w", record, err) 103 } 104 } 105 106 err = mBatcher.Queue( 107 ctx, 108 queryStmt.SQL, 109 v[1], 110 v[2], 111 v[3], 112 hash, 113 ) 114 if err != nil { 115 return fmt.Errorf("batch insert failed for package record %v: %w", record, err) 116 } 117 118 } 119 err = mBatcher.Done(ctx) 120 if err != nil { 121 return fmt.Errorf("final batch insert failed: %w", err) 122 } 123 indexManifestCounter.WithLabelValues("query_batch").Add(1) 124 indexManifestDuration.WithLabelValues("query_batch").Observe(time.Since(start).Seconds()) 125 126 err = tx.Commit(ctx) 127 if err != nil { 128 return fmt.Errorf("failed to commit tx: %w", err) 129 } 130 return nil 131 } 132 133 // toValues is a helper method which checks for 134 // nil pointers inside an IndexRecord before 135 // returning an associated pointer to the artifact 136 // in question. 137 // 138 // v[0] source package id or nil 139 // v[1] package id or nil 140 // v[2] distribution id or nil 141 // v[3] repository id or nil 142 func toValues(r claircore.IndexRecord) ([4]*uint64, error) { 143 res := [4]*uint64{} 144 145 if r.Package.Source != nil { 146 id, err := strconv.ParseUint(r.Package.Source.ID, 10, 64) 147 if err != nil { 148 return res, fmt.Errorf("source package id %v: %v", r.Package.ID, err) 149 } 150 res[0] = &id 151 } 152 153 if r.Package != nil { 154 id, err := strconv.ParseUint(r.Package.ID, 10, 64) 155 if err != nil { 156 return res, fmt.Errorf("package id %v: %v", r.Package.ID, err) 157 } 158 res[1] = &id 159 160 } 161 162 if r.Distribution != nil { 163 id, err := strconv.ParseUint(r.Distribution.ID, 10, 64) 164 if err != nil { 165 return res, fmt.Errorf("distribution id %v: %v", r.Distribution.ID, err) 166 } 167 res[2] = &id 168 } 169 170 if r.Repository != nil { 171 id, err := strconv.ParseUint(r.Repository.ID, 10, 64) 172 if err != nil { 173 // return res, fmt.Errorf("repository id %v: %v", r.Package.ID, err) 174 return res, nil 175 } 176 res[3] = &id 177 } 178 179 return res, nil 180 }