Reply to thread

This is a rather complicated bug, but I hope it's understandable.


Method XF\Service\User\Delete::delete sets entity options to avoid running jobs XF:UserRenameCleanup and XF:UserDeleteCleanup when renaming a user before deletion:

[php]

if ($this->renameTo)

{

    $user->reset();

    $user->setOption('admin_edit', true);

    $user->setOption('enqueue_rename_cleanup', false);

    $user->setOption('enqueue_delete_cleanup', false);


    $user->set('username', $this->renameTo);

[/php]


After deletion,. those jobs are enqueued in method runPostDeleteJobs

[php]

if ($this->renameTo)

{

    $jobList = [

        [

            'XF:UserRenameCleanUp',

            [

                'originalUserId' => $user->user_id,

                'originalUserName' => $this->originalUserName,

                'newUserName' => $this->renameTo

            ]

        ],

        [

            'XF:UserDeleteCleanUp',

            [

                'userId' => $user->user_id,

                'username' => $this->renameTo

            ]

        ]

    ];

    $this->app->jobManager()->enqueueUnique('userRenameDelete' . $user->user_id, 'XF:Atomic', [

        'execute' => $jobList

    ]);

[/php]


Job XF:UserRenameCleanUpcalls service XF:User\ContentChange in method run

[php]

/** @var \XF\Service\User\ContentChange $contentChanger */

$contentChanger = $this->app->service(

        'XF:User\ContentChange', $this->data['originalUserId'], $this->data['originalUserName']

);

$contentChanger->setupForNameChange($this->data['newUserName']);

$contentChanger->restoreState($this->data['currentStep'], $this->data['lastOffset']);


$result = $contentChanger->apply($maxRunTime);

[/php]


Job XF:UserDeleteCleanUp calls service XF:User\DeleteCleanUp in method run

[php]

$deleter = $this->app->service(

        'XF:User\DeleteCleanUp', $this->data['userId'], $this->data['username']

);

$deleter->restoreState($this->data['currentStep'], $this->data['lastOffset']);


$result = $deleter->cleanUp($maxRunTime);

[/php]

 

Now finally service XF:User\DeleteCleanUp calls service XF:User\ContentChange in method stepChangeOwne

[php]

$contentChanger = $this->service('XF:User\ContentChange', $this->userId, $this->userName);

$contentChanger->setupForDelete();


if (is_array($lastOffset))

{

    list($changeStep, $changeLastOffset) = $lastOffset;

    $contentChanger->restoreState($changeStep, $changeLastOffset);

}


$result = $contentChanger->apply($maxRunTime);

[/php]


The overall effect of this call chain is that content is changed twice:


  1. First, only the username is changed to $newUsername via rename cleanup
  2. Afterwards the userid is changed to 0 via delete cleanup


This is inefficient and can cause quite some performance issues as noted in

[URL unfurl="true"]https://xenforo.com/community/threads/pruned-25k-members-and-now-the-server-is-grinding-to-a-halt.208030/post-1631745[/URL]

[URL unfurl="true"]https://xenforo.com/community/threads/too-many-mysql-processes-update-select-content_id-from-xf_reaction_content-where-content_type-and-reaction_.179637/post-1463518[/URL]


In case a user account is renamed before deletion there should be only one content change:

Update userid to 0 and set username to $newUsername


Back
Top Bottom