Mage2Plenty v3.10 - Self-Healing Stock-Drift Detection & Faster Incremental Sync
Mage2Plenty v3.10 adds an automatic stock-drift detection, auto-fix and weekly report suite that keeps Magento and PlentyONE physical quantities in agreement, eliminates plenty_stock_export_queue deadlocks under concurrent load, and makes incremental sync noticeably faster by indexing the collected_at watermark on every mirror table. It also hardens routine maintenance with batched, memory-safe cleanup across history, queue and orphaned stock.
Stock Accuracy
Self-healing physical-drift detection
Physical stock can quietly diverge between systems — a missed event, a manual edit, a failed batch — and the gap usually only surfaces when a customer orders something that isn't really there. v3.10 introduces a stock-consistency suite that finds and closes those gaps on its own.
DriftInspectoris a read-only, direction-agnostic detector that compares Magento physical (inventory_source_item.quantity) against PlentyONE physical (plenty_stock_entity.stock_physical) for every mapped source/warehouse pair.- An hourly tracker (
plenty_stock_drift_log_sync, default35 * * * *) records each discrepancy's persistence in the newplenty_stock_drift_logtable and attempts a direction-aware auto-fix (re-collect-then-re-pend for import, enqueue for export), so transient drift self-heals before anyone notices. - A weekly report (
plenty_stock_drift_report, default Monday06:00) escalates only the drift that persisted past a configurable threshold (default 24 hours) — so genuinely stuck SKUs reach you, and a clean week sends nothing.
The report is delivered through a new stock physical-drift report email in the notification module, sent to your configured notification recipients.
On-demand drift report
For ad-hoc checks there's a read-only CLI that runs the same inspection without changing anything:
# Inspect the import-primary scope (import profile, plus export when active)
bin/magento plenty:stock:drift:report
# Inspect a single profile
bin/magento plenty:stock:drift:report --profile=<id>
Orphan & stale stock cleanup
Two new maintenance commands (and their crons) keep plenty_stock_entity free of rows that can otherwise inflate warehouse totals:
# Remove rows whose variation no longer exists in PlentyONE (age-guarded)
bin/magento plenty:stock:cleanup:orphan
# Remove rows for warehouses no longer in the local PlentyONE warehouse config
bin/magento plenty:stock:cleanup:warehouse
The plenty:stock:cleanup:orphan logic is shared with a scheduled plenty_stock_orphan_cleanup cron — disabled by default because it is destructive (enable it once you've confirmed the dry behaviour, with a 14-day retention guard). All stock-maintenance crons — reservation cleanup, export-drift reconcile, drift detection, drift report and orphan cleanup — now live under the same plenty/stock_config/* convention, each with its own enable toggle and schedule so you can turn any of them off independently of stock sync.
Export queue: no more deadlocks
Under heavy concurrent load — the high-volume Plenty stock import, order shipments, admin/CSV saves and the drift reconciler all writing at once — plenty_stock_export_queue could deadlock on its UNIQUE(sku) index, surfacing as SQLSTATE[40001] ... Deadlock found in the logs. v3.10 fixes this at the single shared writer:
- Deterministic lock ordering — multi-row upserts are sorted by SKU, so every statement locks the unique index in the same order and overlapping batches no longer invert.
- Safe retry — deadlock/lock-wait retries apply only when the upsert is its own autocommit statement; inside a caller's transaction the error is allowed to propagate so a rolled-back transaction is never partially re-committed.
On top of that, the queue is now gated on the export schedule: when the stock-export schedule is inactive (both the schedule record and the profile schedule status), automatic producers stop enqueueing instead of piling up rows that will never be drained. Explicit CLI and manual actions still bypass the gate, so ad-hoc exports keep working with the schedule switched off.
Performance
Faster incremental sync
Incremental collection asks "what changed since the last run?" by reading the newest collected_at watermark on each PlentyONE mirror table. Until now that lookup scanned and sorted the whole table. v3.10 adds a dedicated collected_at btree index to every mirror table — attribute, category, client, customer, item, log, order, property and stock — and limits the watermark query (GetLastCollectedAt) to a single row.
The result: the per-table "since last run" lookup that drives every scheduled collection is now index-backed instead of a full scan, so collection starts faster and stays fast as catalogues and history grow.
Maintenance & Stability
Batched, memory-safe cleanup
Long-running stores accumulate history, queue and log rows that eventually slow housekeeping down or exhaust memory when purged in one shot. v3.10 adds a shared BatchPurgeTrait in the core module for batched, age-based deletion and applies it across the suite:
- Profile history cleanup now batches its deletes and indexes
created_at. - Profile queue cleanup now batches its deletes.
- A new Magento-deleted item orphan cleanup cron prunes mirror rows for products that no longer exist in Magento.
These keep maintenance jobs bounded and predictable on large installations.
Bug Fixes
- Image import without
Magento_Downloadable— product image import resolvesDomainManagerInterfaceat runtime, but the only binding shipped inMagento_Downloadable. With that module disabled, import failed with "Cannot instantiate interface". The core module now binds it, so image import works regardless of the downloadable module's state. - Null manufacturer country id — attribute import no longer fails when a manufacturer has no country id set.
Release Summary
| Module | Version | Bump | Key Changes |
|---|---|---|---|
module-core | 2.4.1 → 2.5.0 | minor | BatchPurgeTrait for batched DB cleanup; bind DomainManagerInterface so image import works without Magento_Downloadable |
module-plenty-stock-profile | 2.5.0 → 2.6.0 | minor | Stock drift detection, auto-fix & weekly report; orphan and deleted-warehouse stock cleanup |
module-plenty-item-profile | 3.4.1 → 3.5.0 | minor | Magento-deleted item orphan cleanup cron; guard null manufacturer country id on import |
module-profile-notification | 2.1.2 → 2.2.0 | minor | Stock physical-drift report email |
module-plenty-stock | 2.2.1 → 2.2.2 | patch | Prevent plenty_stock_export_queue deadlocks and gate enqueue on schedule; index collected_at |
module-profile-history | 2.0.1 → 2.0.2 | patch | Batch history cleanup and index created_at |
module-profile-queue | 2.0.1 → 2.0.2 | patch | Batch queue cleanup |
module-plenty-profile | 2.2.1 → 2.2.2 | patch | Limit GetLastCollectedAt watermark query to a single row |
| Incremental-sync indexing | patch | patch | collected_at index added to attribute, category, client, customer, item, log, order and property mirror tables |
The collected_at index ships in each of those module repos individually (module-plenty-attribute 2.0.5, -category 2.2.3, -client 2.1.5, -customer 2.1.4, -item 2.4.3, -log 2.0.3, -order 2.2.1, -property 2.0.6). module-plenty-order-profile (2.7.1) and module-profile-schedule (2.0.4) ship as internal maintenance bumps.
Metapackage: softcommerce/mage2plenty-os 3.9.0 → 3.10.0
Upgrade Guide
composer require softcommerce/mage2plenty-os:^3.10
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
This release does ship schema changes — the new plenty_stock_drift_log table and collected_at indexes across the mirror tables — so setup:upgrade will apply them. On very large tables, adding the indexes can take a few moments; plan the upgrade accordingly.
Default-on-upgrade behaviours worth noting:
- Drift detection and the weekly drift report are enabled by default (hourly tracker, Monday report). Adjust or disable them under Stock Settings on the stock-export profile.
- Orphan stock cleanup is disabled by default — it is destructive; review and enable it deliberately.
- Export enqueue is now gated by the stock-export schedule. If you run stock export only via explicit CLI/manual actions (schedule off), nothing changes for you; automatic producers simply stop queueing rows that wouldn't be drained.
module-plenty-stock-profilenow depends onmodule-profile-notificationfor the drift report email.
Resources
Questions about the upgrade? Reach out to us at support@byte8.io.
