Documentation
fsds/bbu-oracle-processor-race-condition-fix.md
Fix Race Condition in OracleProcessor ItemType/Item Creation
Problem Summary
The OracleProcessor has a race condition causing FK violations when creating Items. The error sequence is:
- "duplicate key value violates unique constraint IX_item_types_name"
- "ItemType X was created by another worker, looking up existing"
- "insert or update on table items violates foreign key constraint FK_items_item_types_item_type_id"
Root Cause
Primary Issue: The itemTypeMap returned by BatchUpsertItemTypesAsync is computed but NEVER USED.
Line 1113: var itemTypeMap = await BatchUpsertItemTypesAsync(...); // Returns verified map
Line 1220: await SyncStackProductItemsWithContextAsync(...); // Does NOT receive itemTypeMap!
Instead, SyncStackProductItemsWithContextAsync performs its own independent database query (lines 1927-1935), creating a race condition window where the newly created ItemType/identifier may not be visible yet.
Secondary Issue: The unique constraint on item_types.name is NOT tenant-scoped, causing cross-tenant conflicts.
Implementation Plan
Stage 1: Pass itemTypeMap to SyncStackProductItemsWithContextAsync
File: engines/bbu/src/Acsis.Dynaplex.Engines.Bbu/Services/OracleProcessor.cs
- Update method signature (line 1888) - add
IReadOnlyDictionary<string, Guid> itemTypeMapparameter - Update call site (line 1220) - pass
itemTypeMapto the method
Stage 2: Remove Redundant Database Lookup
File: engines/bbu/src/Acsis.Dynaplex.Engines.Bbu/Services/OracleProcessor.cs
- Remove lines 1923-1935 (the independent SKU identifier lookup)
- Change line 1969 from
itemTypeIdsByCode.TryGetValuetoitemTypeMap.TryGetValue
Stage 3: Improve Error Logging
File: engines/bbu/src/Acsis.Dynaplex.Engines.Bbu/Services/OracleProcessor.cs
Update the logging when itemTypeId is not found to clearly indicate this is a bug:
_logger.LogError(
"Item type for {ItemCode} not found in pre-verified itemTypeMap. " +
"This indicates a bug in BatchUpsertItemTypesAsync. Stack: {StackId}",
detail.ItemAlternateCode, stackItem.Id);
Stage 4: Fix Tenant-Scoped Unique Index
File: engines/catalog/src/Acsis.Dynaplex.Engines.Catalog.Database/ItemType.cs
Change line 17:
// FROM:
[Index(nameof(Name), IsUnique = true)]
// TO:
[Index(nameof(TenantId), nameof(Name), IsUnique = true)]
Stage 5: Create Database Migration
Use /generate-migration to create migration that:
- Drops
IX_item_types_name - Creates
IX_item_types_tenant_id_nameon(tenant_id, name)
Files to Modify
| File | Changes |
|---|---|
engines/bbu/src/Acsis.Dynaplex.Engines.Bbu/Services/OracleProcessor.cs |
Add parameter, update call site, remove redundant lookup, improve logging |
engines/catalog/src/Acsis.Dynaplex.Engines.Catalog.Database/ItemType.cs |
Fix unique index to be tenant-scoped |
engines/catalog/src/Acsis.Dynaplex.Engines.Catalog.Database/Migrations/ |
New migration file (generated) |
Why This Fixes the Bug
- Eliminates race window: The verified
itemTypeMapfromBatchUpsertItemTypesAsyncis used directly - no gap where data can become stale - Single source of truth: ItemType IDs are looked up once during creation, then reused immediately
- Better error handling: If an ItemType creation fails, it fails early with clear logging
- Tenant isolation: Each tenant can have their own ItemTypes with the same names
Risk Assessment
- Low risk: Primary changes are parameter passing and removing redundant code
- Migration risk: The index change is less restrictive, so existing data should be valid
- Rollback: Both code and migration can be reverted if issues arise