@@ -2,6 +2,7 @@ package models
22
33import (
44 "context"
5+ "sync"
56 "time"
67
78 // Postgres Database Driver
@@ -59,6 +60,88 @@ func GetProductfileObject(db *pgxpool.Pool, ID uuid.UUID) (*ProductfileObject, e
5960 return & obj , nil
6061}
6162
63+ // --- Cached productfile -> {bucket, key} lookup ------------------------------------------------
64+ //
65+ // The COG proxy (StreamProductfileCOG) resolves the S3 bucket+key on EVERY Range request, and a
66+ // single client import fires thousands of them. The mapping is immutable for a given productfile id
67+ // (its file column never changes, and write_to_bucket is a process-constant config value), so it is
68+ // safe to memoize — keeping the 15-connection Postgres pool out of the per-read hot path.
69+
70+ // productfileCacheMax bounds the in-memory key cache so a long-running process can't grow without
71+ // limit. Entries are ~an id + a short key string; the cap is ~tens of MB.
72+ const productfileCacheMax = 200000
73+
74+ var (
75+ pfCacheMu sync.RWMutex
76+ pfCacheBucket string
77+ pfCacheKeys = make (map [uuid.UUID ]string )
78+ )
79+
80+ // GetProductfileObjectCached returns the S3 bucket+key for a productfile id, memoized. On a miss it
81+ // falls back to the database (one cheap scalar query); the write_to_bucket bucket is read once and
82+ // reused. Safe for concurrent use.
83+ func GetProductfileObjectCached (db * pgxpool.Pool , ID uuid.UUID ) (* ProductfileObject , error ) {
84+ pfCacheMu .RLock ()
85+ key , keyOK := pfCacheKeys [ID ]
86+ bucket := pfCacheBucket
87+ pfCacheMu .RUnlock ()
88+
89+ if keyOK && bucket != "" {
90+ return & ProductfileObject {Bucket : bucket , Key : key }, nil
91+ }
92+
93+ // Resolve the bucket once (immutable config value).
94+ if bucket == "" {
95+ b , err := getWriteToBucket (db )
96+ if err != nil {
97+ return nil , err
98+ }
99+ bucket = b
100+ pfCacheMu .Lock ()
101+ pfCacheBucket = b
102+ pfCacheMu .Unlock ()
103+ }
104+
105+ // Resolve + cache the key (immutable per id). A rare concurrent double-miss just queries twice
106+ // and stores the same value — harmless.
107+ if ! keyOK {
108+ k , err := getProductfileKey (db , ID )
109+ if err != nil {
110+ return nil , err
111+ }
112+ key = k
113+ pfCacheMu .Lock ()
114+ // Bound memory over a long uptime. Entries are immutable and cheap to repopulate, so a crude
115+ // drop-all on overflow is safe (subsequent reads simply re-query).
116+ if len (pfCacheKeys ) >= productfileCacheMax {
117+ pfCacheKeys = make (map [uuid.UUID ]string )
118+ }
119+ pfCacheKeys [ID ] = k
120+ pfCacheMu .Unlock ()
121+ }
122+
123+ return & ProductfileObject {Bucket : bucket , Key : key }, nil
124+ }
125+
126+ func getWriteToBucket (db * pgxpool.Pool ) (string , error ) {
127+ var bucket string
128+ err := db .QueryRow (
129+ context .Background (),
130+ `SELECT config_value FROM config WHERE config_name::text = 'write_to_bucket'::text` ,
131+ ).Scan (& bucket )
132+ return bucket , err
133+ }
134+
135+ func getProductfileKey (db * pgxpool.Pool , ID uuid.UUID ) (string , error ) {
136+ var key string
137+ err := db .QueryRow (
138+ context .Background (),
139+ `SELECT file FROM productfile WHERE id = $1` ,
140+ ID ,
141+ ).Scan (& key )
142+ return key , err
143+ }
144+
62145// ListProductfiles returns array of productfiles
63146func ListProductfiles (db * pgxpool.Pool , ID uuid.UUID , after string , before string ) ([]Productfile , error ) {
64147 ff := make ([]Productfile , 0 )
0 commit comments