Date: 2026-04-08
Affected user (example): bart6446 (bartoffermans@gmail.com, userId: 6876c28bc2fa8516b1a41cf4)
Impact: Portfolio value dropped from ~46.7k to ~41k (~12% loss)
The originally suspected root cause (PriceBulkExtractorService missing fallback) was already fixed on April 3rd (commit beea7c2fa). The actual bug is that OpenSearch inventory documents have stale priceEu values that don't match the (correct) ProductView data. The break is in the ProductView -> OpenSearch inventory sync.
| Layer | priceEu |
Source |
|---|---|---|
| prices collection | marketValue=0, low=8.57 | db.prices.find({productId, isLatest: true}) |
| ProductView (MongoDB) | 8.57 | Fallback correctly applied (low) |
| OpenSearch (inventory_v5) | 0 | Stale/incorrect |
| Layer | priceEu |
Source |
|---|---|---|
| prices collection | marketValue=0, low=0.20 | db.prices.find({productId, isLatest: true}) |
| ProductView (MongoDB) | 0.20 | Fallback correctly applied (low) |
| OpenSearch (inventory_v5) | 0 | Stale/incorrect |
For bart6446 alone: 247 inventory items have priceEu=0 in OpenSearch while owning quantity > 0. Many of these likely have valid prices in ProductView.
PriceBulkExtractorService(incremental price path) - Fixed April 3rd, usesgetEffectivePrice()with full fallback chain (marketValue -> low -> mid -> high)createProductNexusPriceStages()(full ProductView refresh path) - Always had the fallback viacreatePriceFallbackExpression()in MongoDB aggregation- ProductView documents - Currently contain correct
priceEuvalues with fallback applied
The pipeline: ProductView (MongoDB) -> OpenSearch inventory documents
Three code paths write to OpenSearch inventory:
| Path | File | Trigger |
|---|---|---|
| Partial update | inventory-product-update.handler.ts |
product-view.refreshed event |
| Full reindex | inventory-reindex-migration.service.ts |
Schema version bump |
| Full indexing | inventory-document-assembler.service.ts |
Initial indexing |
All three read productView.pricesByFinish[finish].priceEu and write it to marketData.priceEu in OpenSearch.
On April 7th, the inventory schema version was bumped to v3 (commits adding expansionId to inventory index). This triggered a full reindex of all inventory documents. During this reindex:
- Either the ProductView didn't have the correct fallback values at that moment (timing/race condition with ProductView refresh)
- Or the reindex completed, and a subsequent price import wrote
priceEu: 0via the partial update path before the ProductView was refreshed with fallback values
After the reindex, the partial update path (product-view.refreshed event) should have corrected the values when ProductView was next refreshed - but apparently it didn't propagate to all 247+ items.
Trigger a targeted OpenSearch inventory refresh for affected documents. The ProductView data is already correct - we just need to sync it to OpenSearch.
Options:
- Re-run the inventory reindex migration (heavy but thorough)
- Trigger
product-view.refreshedevents for products withpricesByFinish[finish].priceEu > 0where OpenSearch hasmarketData.priceEu = 0
Why didn't the partial update path (inventory-product-update.handler.ts) correct OpenSearch after ProductView was refreshed? Possible causes:
- Event not emitted - ProductView refresh didn't fire
product-view.refreshedfor these products - Event lost - The event was emitted but the handler didn't process it (message queue issue?)
- Scroll/pagination gap - The handler uses OpenSearch scroll to find matching inventory docs; some may have been skipped
- Race condition - A subsequent operation overwrote the corrected values
packages/backend/domains/pricing/src/price-bulk-extractor.service.ts(lines 128-129) - Price extraction with fallback (already fixed)packages/core/db/src/utils/price-helpers.ts(lines 152-161) -getEffectivePrice()functionpackages/core/db/src/pipeline-builder/lookup.helper.ts(lines 74-173) -createProductNexusPriceStages()packages/backend/features/search/src/inventory/inventory-partial-assembler.service.ts(lines 102-112) - Partial update builderpackages/backend/domains/inventory/src/sync/handlers/inventory-product-update.handler.ts- Event-driven syncpackages/backend/domains/inventory/src/sync/inventory-reindex-migration.service.ts(lines 460-519) - Full reindexpackages/backend/features/search/src/transforms/inventory-transform.ts(lines 223-233) - OpenSearch document transform