Details
-
Type:
New Feature
-
Status:
Resolved
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: None
-
Fix Version/s: 2.0-BETA3
-
Component/s: None
-
Security Level: All
-
Labels:None
Description
In some scenarios it is necessary to explicitly lock rows for update in a select query. My idea would be to support it twofold:
1. Add a LockMode Class:
final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; }
2. Add methods to platforms that can add necessary READ/WRITE Lock query additions like FOR UPDATE/SHARED which afaik are supported by all rmdbs in some way.
3. Add a query hint "lockMode" which takes a Lock constant.
4. Add a query hint "lockVersion" which takes an integer or timestamp versioning value.
5. Change DQL Parser to apply lock mode if set, and if lock_mode = optimistic, add a where clause for the only top-level class (more not supported? Reread Evans DDD / Aggregates)
6. Change the $em->find() method to the following signature:
public function find($class, $identifier, $lockMode=0, $lockVersion=null);
And if the values are set, set the appropriate query hints.
Updated API specification
LockModes
final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; }
Constraints
- LockMode::OPTIMISTIC requires entities to be versioned
- LockMode::PESSIMISTIC_READ/WRITE works similarly for versioned as well as non-versioned entities. However, lock() after read only works for versioned entities.
API spec
$query->setLockMode(LockMode::OPTIMISTIC)
Effects:
- Throw OptimisticLockException if any of the entities fetched (or fetch-joined) by the query are not versioned.
- Otherwise proceed normally, SQL is not modified.
$query->setLockMode(LockMode::PESSIMISTIC_READ/WRITE)
Effects:
- Throw TransactionRequiredException if there is no running transaction.
- Modify the SQL with an appropriate locking clause (i.e. FOR UPDATE) that can be platform-specific, to acquire a pessimistic lock on all read entities.
- Throw PessimisticLockException if lock(s) could not be obtained.
$em->find($entity, LockMode::OPTIMISTIC) (or findBy et al)
Effects:
- Throw OptimisticLockException if entity is not versioned.
- Otherwise proceeed normally.
$em->find($entity, LockMode::OPTIMISTIC, $version) (or findBy et al)
Effects:
- Throw OptimisticLockException if entity is not versioned.
- find() entity normally, no SQL modification.
- Throw OptimisticLockException if there is a version mismatch ($version != $entity->version)
- Otherwise proceed normally.
$em->find($entity, LockMode::PESSIMISTIC_READ/WRITE)
Effects:
- Throw TransactionRequiredException if there is no active transaction.
- Modify the SQL to incude an appropriate platform-specific pessimistic lock (i.e. FOR UPDATE)
- throw PessimisticLockException If lock could not be obtained
- Otherwise proceed normally
- Refresh entity with lock when entity with id exists in identity map already
$em->refresh($entity, LockMode::OPTIMISTIC)
Effects:
- Throw OptimisticLockException if entity is not versioned.
- Otherwise proceed normally. (What about cascades here? Need to take that into account probably)
$em->refresh($entity, LockMode::PESSIMISTIC_READ/WRITE)
Effects:
- Throw TransactionRequiredException if there is no active transaction.
- Ensure the SQL used for refreshing is modified accordingly with the platform-specific pessimistic locking clause.
- Throw PessimisticLockException if the lock could not be obtained.
- Otherwise proceed normally.
$em->lock($entity, LockMode::OPTIMISTIC)
Effects:
- Throw OptimisticLockException if entity is not versioned.
- Otherwise do nothing (NOOP).
$em->lock($entity, LockMode::PESSIMISTIC_READ/WRITE)
Effects:
- Throw TransactionRequiredException if there is no active transaction.
- Throw PessimisticLockException if entity is not versioned (this is always a "lock after read")
- Issue straight, minimal locking SQL (we probably must include the version column in the select), platform-specific. Note: Probably get the SQL from the persisters to account for different inheritance mapping strategies. The last part of the SQL, the locking clause, is taken from the platforms.
- Throw PessimisticLockException if the lock could not be obtained.
- Compare the newly read version with the old version. If they dont match throw PessimisticLockException (this means the entity was changed since it was read).
- Otherwise proceed normally.
Issue Links
- relates to
-
DDC-410
review all sql to ensure that it works with concurrent requests
-