## Problem/Motivation
`node_revision_delete_update_21001()` ("Backfill the queue map table from existing queue items") does a **blind multi-row `INSERT`** into `queue_node_revision_delete`, whose `nid` column is the PRIMARY KEY:
```php
$insert = $database->insert(NodeRevisionDeleteInterface::QUEUE_SEMAPHORE_TABLE)
->fields(['nid']);
foreach ($items as $item) {
$nid = unserialize($item->data, ['allowed_classes' => FALSE]);
if (is_int($nid)) {
$insert->values([$nid]);
}
...
}
```
There is no guard against a nid that is already in the map table. But the runtime queueing path (`NodeRevisionDelete::insert()`, guarded by an existence check) already populates that same table when nodes are enqueued during normal cron/node-save activity. So if any queueing happened on the 2.1.x code before the update ran, the map table already contains those nids, and the backfill dies with:
```
SQLSTATE[23000]: Integrity constraint violation: 1062
Duplicate entry '30066' for key 'PRIMARY':
INSERT INTO "queue_node_revision_delete" ("nid") VALUES ...
```
Because Drupal stops at the first failing update hook, this blocks `drush updatedb` entirely (and any CI/build pipeline that runs updates — this was hit on a Probo build). The same crash also occurs if a node is enqueued more than once, or if the update is simply re-run after a partial failure.
## Steps to reproduce
1. On 2.1.x, have nodes enqueued for revision deletion such that `queue_node_revision_delete` is populated (normal cron does this).
2. Run `drush updatedb`.
3. `node_revision_delete_update_21001` aborts with the duplicate-PRIMARY-key integrity constraint violation above.
## Proposed resolution
Make the backfill idempotent: for each batch, select which candidate nids already exist in the map table and only insert the missing ones (also de-duplicating within the batch). This mirrors the "respect IDs already present" approach already used by `update_21003` in the MR for #3611089.
```php
if ($nids) {
$existing = $database->select(NodeRevisionDeleteInterface::QUEUE_SEMAPHORE_TABLE, 'm')
->fields('m', ['nid'])
->condition('nid', $nids, 'IN')
->execute()
->fetchCol();
$nids = array_diff_key($nids, array_flip($existing));
}
```
Patch attached. Verified against a real site DB (2881 queued nodes, map table already fully populated): the update previously aborted at `21001` and now completes cleanly with the map table intact (2881/2881 distinct nids, no data lost).
## Related
- #3611089 "Duplicate items in Node revision delete requeue" (MR !77) reworks this area and adds `update_21002`/`update_21003`, but leaves `update_21001` unchanged, so it does not fix this crash.
| Comment | File | Size | Author |
|---|---|---|---|
| update-21001-idempotent-backfill.patch | 1.47 KB | dzinkevich |
Issue fork node_revision_delete-3616155
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
pfrenssenThanks @dzinkevich for creating the bug report and contributing a patch!
Drupal.org has moved to using GitLab merge requests and the patch based workflow is no longer supported. No automated tests will run, and in practice most maintainers will prioritize issues that have a merge request. For more information how to use GitLab, see Using GitLab to contribute to Drupal.
I have opened a merge request and committed the original patch. Still needs review.
Comment #5
pfrenssenI had a project that was affected by this issue, there was 1 row present in the table. I was not able to exactly identify how this happened, but I presume it was due to a node save done by a background task or a user with permission to bypass maintenance mode.
I have reviewed the patch. I have also replicated the issue locally and can confirm it solves the issue and correctly skips over the existing rows.