In hook user presave I implemented thread safe logic, based on Locker service.
// Thread safe locking for ensuring correct user id.
while (!$this->lockBackend->acquire(static::LOCK)) {
// Wait 0.1 second for next attempt.
$this->lockBackend->wait(static::LOCK, 0.1);
}
// Use try/finally to guarantee lock release.
try {
// Lock acquired.
... some logic
}
finally {
// Release lock.
$this->lockBackend->release(static::LOCK);
}
I wrote simple web test for this that emulates concurrent threads for user creration:
$users = User::loadMultiple();
// 2 users already
$this->assertCount(2, $users, "User count is 2");
$i = 0;
$usersCount = 5;
$requests = [];
while ($i < $usersCount) {
// drupal get async is custom method, based on built in httpClient.
// path '/consistent_uid_test/user_create_handler' handler creates user.
$requests[] = $this->drupalGetAsync('/consistent_uid_test/user_create_handler/' . $this->getRandomGenerator()->name(8, TRUE) . '/' . sha1('keep_calm_dude'));
$i++;
}
\GuzzleHttp\Promise\all($requests)->wait();
$users = User::loadMultiple();
$this->assertCount($usersCount + 2, $users, "User count is {$usersCount}");
so SOMETIMES this success and sometimes:
there is an exception: Drupal\Core\Entity\EntityStorageException: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction: INSERT INTO {semaphore} (name, value, expire) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2); Array
(
[:db_insert_placeholder_0] => consistent_uid.lock
[:db_insert_placeholder_1] => 4243437945b68c6bb5f5827.54321271
[:db_insert_placeholder_2] => 1533593305.3904
)
in .....
As i see there is no special handling for this kind of exception in locker class. And this is not documented and breaks all locking logic.
Is locking system safe?
Comments
Comment #2
olexyy.mails@gmail.com commentedComment #3
cilefen commentedComment #4
olexyy.mails@gmail.com commentedLooks like simple wrappers done work for this load test. Switched to 100 parallel threads.
All this does is simply repeat attempt on deadlock. All is according to MySQL docs =)))).
Set $usersCount = 100;
We need to understand that this emulates 100 parallel threads to one server with each requesting user creation.
As test case forms queue of 100 requests, and method Guzzle wait() executes them all at once.
Comment #5
olexyy.mails@gmail.com commentedSee consistent_uid module for details.
Very weird that locking system does not pass high load tests by default.
Comment #6
olexyy.mails@gmail.com commentedComment #7
olexyy.mails@gmail.com commentedComment #18
luke.stewart commentedThis has sat untouched for a while. It's now the daily target for the Bug Smash Initiative.
I'm marking PMNMI, and Needs Issue Summary Update.
I think this issue requires a clearer statement of the problem, and an updated issue summary to reflect that. Otherwise this can probably be closed in 3 months.