Key Takeaways
- Why directly connecting readers to Odoo is an anti-pattern
- Gateway architecture: a three-layer pipeline
- stock.quant write hooks and conflict handling
01Why directly connecting readers to Odoo is an anti-pattern
Readers can output hundreds to thousands of TagRead events per second, full of duplicates (the same tag read 5-20 times within 1 second). Writing these directly to Odoo instantly overwhelms database locks — concurrent writes to stock.quant cause PostgreSQL row-lock queues that freeze the entire inventory module. A 4-antenna reader over a conveyor generates 2000+ raw events per second, 95% of which are duplicate reads. The correct approach is a gateway middleware between the reader and Odoo that handles deduplication, aggregation, and buffering before batch-writing.
- 4-antenna conveyor: 2000+ events/sec, 95% duplicates
- Direct write to Odoo: PostgreSQL row-lock queue → inventory freeze
- Gateway middleware: dedup → aggregate → buffer → batch write
02Gateway architecture: a three-layer pipeline
The SpidersRFID gateway uses a three-layer pipeline. Layer 1 is the LLRP event receiver, subscribing to TagReadData events via TCP long-connection with a 100ms heartbeat. Layer 2 is the deduplication engine, using a sliding-window algorithm (default 2-second window) with EPC+antenna-ID composite key dedup, compressing 2000 events/sec to 50-80 events/sec. Layer 3 is the Odoo writer, calling stock.quant's write method via XML-RPC/JSON-RPC with batch commits (every 500ms, max 100 per batch) and optimistic concurrency control (OCC) for conflict handling. The three layers are decoupled via Redis queues, ensuring reader disconnections don't affect already-buffered events.
EPC parsing is the critical step after deduplication. The 96-bit EPC Gen2 encoding contains four segments: Header, Domain Manager, Object Class, and Serial Number. The gateway parses the EPC and maps it to Odoo's product.product.external_id field, looking up the corresponding product.product record via ir.model.data. For unmapped EPCs, the gateway writes to a pending-match queue in stock.quant.production.lot for manual binding by the admin. In practice, 5-10% of EPCs are unmapped at first deployment, requiring a 1-2 day commissioning period for full binding.
03stock.quant write hooks and conflict handling
Odoo's stock.quant table is the inventory core — each row represents a location+product+lot quantity. The gateway intercepts stock.quant writes via a custom write hook with three protections: (1) field-level lock — using SELECT FOR UPDATE to lock the target row, preventing concurrent overwrites; (2) incremental write — instead of directly setting quantity, it creates stock.move records, letting Odoo's standard inventory logic handle quantity changes and ensuring stock journal entries and accounting moves are generated correctly; (3) conflict retry — when OCC detects a version conflict (__last_update timestamp mismatch), the gateway re-reads the latest value and retries, up to 3 times. In practice, at 100K writes/day the conflict rate is only 0.3%, all resolved after retry.
- SELECT FOR UPDATE row lock → prevents concurrent overwrite
- stock.move incremental write → journal/accounting entries correct
- OCC retry 3× → 0.3% conflict rate all resolved



