@@ -254,7 +254,7 @@ func (s *EmailService) ProcessReports() error {
254254func (s * EmailService ) getUnprocessedReports (ctx context.Context ) ([]models.Report , error ) {
255255 qStart := time .Now ()
256256 query := `
257- SELECT r.seq, r.id, r.latitude, r.longitude, r.image, r. ts
257+ SELECT r.seq, r.id, r.latitude, r.longitude, r.ts
258258 FROM reports r
259259 INNER JOIN report_analysis ra ON r.seq = ra.seq
260260 LEFT JOIN report_raw rr ON r.seq = rr.report_seq
@@ -278,7 +278,9 @@ func (s *EmailService) getUnprocessedReports(ctx context.Context) ([]models.Repo
278278 var reports []models.Report
279279 for rows .Next () {
280280 var report models.Report
281- if err := rows .Scan (& report .Seq , & report .ID , & report .Latitude , & report .Longitude , & report .Image , & report .Timestamp ); err != nil {
281+ // NOTE: We intentionally do NOT select `reports.image` here.
282+ // The LONGBLOB is lazy-loaded only if/when we actually send an email.
283+ if err := rows .Scan (& report .Seq , & report .ID , & report .Latitude , & report .Longitude , & report .Timestamp ); err != nil {
282284 return nil , err
283285 }
284286 reports = append (reports , report )
@@ -288,6 +290,22 @@ func (s *EmailService) getUnprocessedReports(ctx context.Context) ([]models.Repo
288290 return reports , nil
289291}
290292
293+ func (s * EmailService ) getReportImage (ctx context.Context , seq int64 ) ([]byte , error ) {
294+ qStart := time .Now ()
295+ var img []byte
296+ if err := s .db .QueryRowContext (ctx , `
297+ SELECT image
298+ FROM reports
299+ WHERE seq = ?
300+ LIMIT 1
301+ ` , seq ).Scan (& img ); err != nil {
302+ log .Errorf ("getReportImage error for seq %d (in %s): %v" , seq , time .Since (qStart ), err )
303+ return nil , err
304+ }
305+ log .Infof ("getReportImage loaded seq=%d bytes=%d (in %s)" , seq , len (img ), time .Since (qStart ))
306+ return img , nil
307+ }
308+
291309// getUnprocessedBrandlessPhysicalReports returns physical reports that have no brand_name set.
292310// These are not picked up by aggregate brand notifications and would otherwise be dropped.
293311func (s * EmailService ) getUnprocessedBrandlessPhysicalReports (ctx context.Context , limit int ) ([]models.Report , error ) {
@@ -1010,7 +1028,17 @@ func (s *EmailService) sendEmailsToInferredContacts(ctx context.Context, report
10101028 }
10111029
10121030 // Send emails with analysis data and map image
1013- err := s .email .SendEmailsWithAnalysis (validEmails , report .Image , mapImg , analysis )
1031+ reportImg := report .Image
1032+ if reportImg == nil {
1033+ // Lazy-load blob only once we're committed to sending.
1034+ img , err := s .getReportImage (ctx , report .Seq )
1035+ if err != nil {
1036+ log .Warnf ("Failed to load report image for seq %d: %v; sending email without report image" , report .Seq , err )
1037+ } else {
1038+ reportImg = img
1039+ }
1040+ }
1041+ err := s .email .SendEmailsWithAnalysis (validEmails , reportImg , mapImg , analysis )
10141042 if err != nil {
10151043 return err
10161044 }
@@ -1077,7 +1105,17 @@ func (s *EmailService) sendEmailsForArea(ctx context.Context, report models.Repo
10771105 }
10781106
10791107 // Send emails with analysis data
1080- err := s .email .SendEmailsWithAnalysis (validEmails , report .Image , polyImg , analysis )
1108+ reportImg := report .Image
1109+ if reportImg == nil {
1110+ // Lazy-load blob only once we're committed to sending.
1111+ img , err := s .getReportImage (ctx , report .Seq )
1112+ if err != nil {
1113+ log .Warnf ("Failed to load report image for seq %d: %v; sending email without report image" , report .Seq , err )
1114+ } else {
1115+ reportImg = img
1116+ }
1117+ }
1118+ err := s .email .SendEmailsWithAnalysis (validEmails , reportImg , polyImg , analysis )
10811119 if err != nil {
10821120 return err
10831121 }
0 commit comments