From 1f88416242a585497f35da06e5542c29048e6e1d Mon Sep 17 00:00:00 2001 From: RohanExploit <178623867+RohanExploit@users.noreply.github.com> Date: Sun, 3 May 2026 14:08:16 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20optimize=20field=20officer?= =?UTF-8?q?=20image=20upload=20performance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit optimizes the `upload_visit_images` endpoint in `backend/routers/field_officer.py` by: 1. Integrating the unified `process_uploaded_image` utility for single-pass resizing (1024px) and EXIF stripping. 2. Offloading blocking synchronous file writes to a thread pool using `run_in_threadpool`. 3. Explicitly enforcing the domain-specific 10MB file size limit to prevent storage regressions. These changes significantly reduce event loop blocking and optimize storage efficiency (e.g., ~73% reduction in file size for large test images). Measurements: - Image dimensions reduced from 2000x2000 to 1024x1024. - Storage footprint reduced from ~63KB to ~17KB for sample high-res JPEG. - Non-blocking I/O ensures event loop responsiveness during high-concurrency uploads. --- .jules/bolt.md | 4 ++ .../visit_1_20260503_140357_0.jpg | Bin 0 -> 17014 bytes .../visit_1_20260503_140357_1.jpg | Bin 0 -> 17014 bytes .../visit_1_20260503_140419_0.jpg | Bin 0 -> 17014 bytes .../visit_1_20260503_140419_1.jpg | Bin 0 -> 17014 bytes .../visit_1_20260503_140443_0.jpg | Bin 0 -> 17014 bytes .../visit_1_20260503_140444_1.jpg | Bin 0 -> 17014 bytes .../visit_1_20260503_140536_0.jpg | Bin 0 -> 17014 bytes .../visit_1_20260503_140536_1.jpg | Bin 0 -> 17014 bytes .../visit_2_20260503_140444_0.jpg | Bin 0 -> 17014 bytes backend/routers/field_officer.py | 48 +++++++++--------- 11 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 backend/data/visit_images/visit_1_20260503_140357_0.jpg create mode 100644 backend/data/visit_images/visit_1_20260503_140357_1.jpg create mode 100644 backend/data/visit_images/visit_1_20260503_140419_0.jpg create mode 100644 backend/data/visit_images/visit_1_20260503_140419_1.jpg create mode 100644 backend/data/visit_images/visit_1_20260503_140443_0.jpg create mode 100644 backend/data/visit_images/visit_1_20260503_140444_1.jpg create mode 100644 backend/data/visit_images/visit_1_20260503_140536_0.jpg create mode 100644 backend/data/visit_images/visit_1_20260503_140536_1.jpg create mode 100644 backend/data/visit_images/visit_2_20260503_140444_0.jpg diff --git a/.jules/bolt.md b/.jules/bolt.md index 956273fc..a261283d 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -89,3 +89,7 @@ ## 2026-05-18 - Jaccard Similarity Optimization via Set Arithmetic **Learning:** In retrieval loops calculating Jaccard similarity (e.g. RAG), explicitly building a union set `A.union(B)` is expensive due to memory allocation and population. **Action:** Use the inclusion-exclusion principle $|A \cup B| = |A| + |B| - |A \cap B|$ to calculate union size in O(1) arithmetic time after calculating the intersection. Pre-calculate $|B|$ (token count) to further reduce overhead. Use `isdisjoint()` for fast early-exit. + +## 2026-05-18 - Async Event Loop Blocking in Image Uploads +**Learning:** Performing synchronous image processing (PIL resize) and file I/O (write) directly in FastAPI async handlers blocks the event loop, causing severe latency spikes under load. Unified processing pipelines (resizing/EXIF stripping) should be offloaded to thread pools to maintain responsiveness. +**Action:** Use `run_in_threadpool` for all image processing and file write operations in async endpoints. Ensure specific domain limits (like 10MB vs 20MB) are checked before calling generic utilities. diff --git a/backend/data/visit_images/visit_1_20260503_140357_0.jpg b/backend/data/visit_images/visit_1_20260503_140357_0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_1_20260503_140357_1.jpg b/backend/data/visit_images/visit_1_20260503_140357_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_1_20260503_140419_0.jpg b/backend/data/visit_images/visit_1_20260503_140419_0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_1_20260503_140419_1.jpg b/backend/data/visit_images/visit_1_20260503_140419_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_1_20260503_140443_0.jpg b/backend/data/visit_images/visit_1_20260503_140443_0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_1_20260503_140444_1.jpg b/backend/data/visit_images/visit_1_20260503_140444_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_1_20260503_140536_0.jpg b/backend/data/visit_images/visit_1_20260503_140536_0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_1_20260503_140536_1.jpg b/backend/data/visit_images/visit_1_20260503_140536_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fc47bec846401f5322759755d5293f7b602d4a3 GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fc3s2GCRJz-$MIsRIM*j0KqKFl!~4 z?LaX#3Sy!|$u?rv3NYKO?=a0+sg2F=zq8wHnD^cHafY17B)u$Ink1AGLd8LxN2G)( zOqOM)h>M~qF4d>0V*FmO+sFNZ0QYmQhcd#t5e;%4iAAEBnU-bgVLNV{@eI>4Qy-z? zRn?`YX$wVfM+C<`%aq4svORQ6&lS94_m6$A6skuE~@jyp9ktk!5 zSicAmPj46ZiOiKiET_n&mshHGt&!W%+@fU_w{-?9ddK2)jk=gciAnKwVVU}0(5qQy&=E?d50W%a6>)oa$STfbrBrp@(Rwr<<8bJy-Yd-pZ% zKXCBS;Uhhzhj=gwbfzj&$R@|COCu6Nz)zJ2HJz56{69`^MQ3=R#Cj6Qk# z?D>nAuU@|ypLqN3{fCdAK7UDdQPKHhzJ>kP#YLASwnbJ`T~w+UC*!gb%W(yY%GJtR z-Og>$jN;~&woZ3eUd33js;<`)isz4JkEhZm%l<5^@t?|mhW+XqAwEXM<}pqR$wW`0 z^+rKqdskE9goG3S00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H z24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaNU;qYS00v+H24DaN K{+j{6GyD~m&ItVg literal 0 HcmV?d00001 diff --git a/backend/data/visit_images/visit_2_20260503_140444_0.jpg b/backend/data/visit_images/visit_2_20260503_140444_0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98d1959e2f6228567fc1148391668387d8d2602d GIT binary patch literal 17014 zcmeIwH&7H&9LMqZ_U+#8h2eO&d%+vnom&n)?Fh!k;m}j)z)S~LlM4%3X4+SvU5JG;$>dEb2>r_XszvddFtDMBeBR2;;4 zL`sRmWLaj4xG0L^Qhk~##_#pIecT@ia6jjIC?~8N(IDrMSR|U8Ygv{aw&S)L&oM1C z^ARduRb6V9re&E1H_X2t=PKcvOk~2SO(aekr%o5K#5xr^Hst$IiOFJ>n%mu@59y zu`|7E_ny7`_8&NS=*ZDy$4{I*b^6S?_VXPVE?&BP<=XYG8#iy=?!I&P-u(v;d;9ta z2A@2A_WZ@r%U7>Q-oAVP;p3;zqhB&zRCNBBZ(+Z6anU7-ZIRVX7nK^s$+)b<3S5Db z3bndUw+kCJBiYj0*6GeGsvHj1)c1Ho@#4Y!kxbfH*`I|q{ZrY`uwPvR#K)-EJjTgP zGTNL@oPE MAX_UPLOAD_SIZE: - raise HTTPException( + + # 2. Fast-fail: Validate file size (10MB limit for field officer visits) + # Must check explicitly because process_uploaded_image uses a 20MB default. + image.file.seek(0, 2) + size = image.file.tell() + image.file.seek(0) + if size > MAX_UPLOAD_SIZE: + raise HTTPException( status_code=400, - detail=f"File {image.filename} exceeds maximum size of {MAX_UPLOAD_SIZE / 1024 / 1024:.1f} MB" + detail=f"File exceeds maximum size of {MAX_UPLOAD_SIZE / 1024 / 1024:.1f} MB" ) - + + # 3. Process image (decode, resize, strip, encode) + _, image_bytes = await process_uploaded_image(image) + # Generate secure filename timestamp = datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S') safe_filename = f"visit_{visit_id}_{timestamp}_{idx}.{extension}" file_path = os.path.join(VISIT_IMAGES_DIR, safe_filename) - # Save file - with open(file_path, 'wb') as f: - f.write(content) + # Save file using threadpool to avoid blocking the main event loop + await run_in_threadpool(save_processed_image, image_bytes, file_path) # Store relative path relative_path = os.path.join("data", "visit_images", safe_filename)