Repository navigation
Support optimistic locking #5123
Replies: 14 comments
|
You could use a priority of |
|
Well, ofc i can create the listener myself (currently i use another way to ensure the version gets set). the question is, if optimistic locking is something to provide out of the box and if so, what is the best way to implement it. |
|
I'll be glad to merge such feature (as long as it is optional). |
|
i can make a PR, but i want to discuss different approaches before. the problem is described in the OP: How to make sure the version is submitted? edit: using a listener to force set the version of the fetched entity to |
|
Is there any progress on this feature? Cause I'm very interested in providing "optimistic locking" in my api. And its quite easy with doctrine's "version" annotation. The only problem is that API Platform should enforce the api user to send the version field within the PUT-Request (and not use it's old value). |
|
IMO we should have another validation listener before deserialize. It makes sense to use a different validation group. |
|
But perhaps it is better if we can specify such constraints in the Symfony Serializer? |
|
Any news here? I am facing the same issue. @teohhanhui could you elaborate what you mean by changing the priority? |
I didn't mention anything about priority? |
|
Ah sorry, I mixed things up! |
|
I think the gist of it is, you could use a custom event listener to enforce that the version property must always be sent. |
|
Ok, but when I GET a response containing version=1 and you mean I should increase it every time by 1 and then send it back (if PUT, POST)? |
|
FYI, we did it on one of our API. We did manually with a on each PUT / PATCH requests, we increment the version and send it back. The field is mandatory and if you send a version lower than the one in database, we throw a 409 http error. A small warning, we use I would be glad to have you opinion on that piece of code. It's do the job but it's quite naive for now. class VersionSubscriber implements EventSubscriber
{
/** @var array */
protected $isProcessed = [];
public function getSubscribedEvents()
{
return [
Events::onFlush,
];
}
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
foreach ($em->getUnitOfWork()->getScheduledEntityUpdates() as $entity) {
// get an entity (itself or its Translatable && Versionable parent
if (!$entity instanceof VersionInterface) {
if (in_array(Translation::class, array_keys(class_uses($entity)))) {
$entity = $entity->getTranslatable();
}
if (!$entity instanceof VersionInterface) {
continue;
}
}
// avoid setting twice a version if multiple translations/relations are added
if (isset($this->isProcessed[spl_object_hash($entity)])) {
return;
}
$this->checkVersion($em, $entity);
$this->updateVersion($entity);
$classMetadata = $em->getClassMetadata(get_class($entity));
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($classMetadata, $entity);
}
}
protected function checkVersion(EntityManagerInterface $entityManager, VersionInterface $entity)
{
$changeSet = $entityManager->getUnitOfWork()->getEntityChangeSet($entity);
if (array_key_exists('version', $changeSet)) {
throw new VersionConflictException(Response::HTTP_CONFLICT, sprintf('Version conflict. Please verify your version (current: "%s")', $changeSet['version'][0]));
}
}
protected function updateVersion(VersionInterface $entity)
{
$entity->setVersion($entity->getVersion() + 1);
$this->isProcessed[spl_object_hash($entity)] = true;
}
} |
No, actually, you don't increment the version when using Doctrine ORM's optimistic locking: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/transactions-and-concurrency.html#optimistic-locking
You should send back the same version value as what you've received from the |
Uh oh!
There was an error while loading. Please reload this page.
Basically the optimistic locking support is almost there. You can add a version field with getter/setter to your entity and you can add it to de/normalization. the only thing missing is to make it required.
a quick fix would be to add a listener that sets the version field to
nullafterReadListener, but before theDenormalizeListener, so if you dont provide a version, then doctrine will fail to update and throws an optimistic lock exception. Furthermore the lock exception can be set to 400 response code. And you can add a validation constraint to the version to have nice error messages.All reactions