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
-
Activity
| Field | Original Value | New Value |
|---|---|---|
| Summary | Query Hint for LOCK mechanisms plus support in $em->load() | Query Hint for LOCK mechanisms plus support in $em->find() |
| Attachment | ddc178_pessimistic.diff [ 10225 ] |
| Attachment | ddc178_pessimistic.diff [ 10225 ] |
| Attachment | ddc178_pessimistic.diff [ 10227 ] |
| Fix Version/s | 2.0-BETA2 [ 10050 ] |
| 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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. |
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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec 1) $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. 2) $query->setLockMode(LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. 3) $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. 4) $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. 5) $em->find($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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 6) $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) 7) $em->refresh($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * Throw PessimisticLockException if entity is not versioned. refresh() with a pessimistic lock is a "lock after read" so there might be stale data. Entity must be versioned in order to verify that. Alternatively we could compare *all* columns but thats going too far imho. * 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. * Throw PessimisticLockException if the refreshed version is not equal to the previous version which the entity had prior to the pessimistic refresh. 8) $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). 9) $em->lock($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. |
| 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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec 1) $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. 2) $query->setLockMode(LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. 3) $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. 4) $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. 5) $em->find($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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 6) $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) 7) $em->refresh($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * Throw PessimisticLockException if entity is not versioned. refresh() with a pessimistic lock is a "lock after read" so there might be stale data. Entity must be versioned in order to verify that. Alternatively we could compare *all* columns but thats going too far imho. * 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. * Throw PessimisticLockException if the refreshed version is not equal to the previous version which the entity had prior to the pessimistic refresh. 8) $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). 9) $em->lock($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. |
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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec h4. $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. h4. $query->setLockMode(LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. h4. $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. h4. $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. h4. $em->find($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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 h4. $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) h4. $em->refresh($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * Throw PessimisticLockException if entity is not versioned. refresh() with a pessimistic lock is a "lock after read" so there might be stale data. Entity must be versioned in order to verify that. Alternatively we could compare *all* columns but thats going too far imho. * 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. * Throw PessimisticLockException if the refreshed version is not equal to the previous version which the entity had prior to the pessimistic refresh. h4. $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). h4. $em->lock($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. |
| 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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec h4. $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. h4. $query->setLockMode(LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. h4. $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. h4. $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. h4. $em->find($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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 h4. $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) h4. $em->refresh($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * Throw PessimisticLockException if entity is not versioned. refresh() with a pessimistic lock is a "lock after read" so there might be stale data. Entity must be versioned in order to verify that. Alternatively we could compare *all* columns but thats going too far imho. * 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. * Throw PessimisticLockException if the refreshed version is not equal to the previous version which the entity had prior to the pessimistic refresh. h4. $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). h4. $em->lock($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * 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. |
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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec h4. $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. h4. $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. h4. $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. h4. $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. h4. $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 h4. $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) h4. $em->refresh($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * Throw TransactionRequiredException if there is no active transaction. * Throw PessimisticLockException if entity is not versioned. refresh() with a pessimistic lock is a "lock after read" so there might be stale data. Entity must be versioned in order to verify that. Alternatively we could compare *all* columns but thats going too far imho. * 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. * Throw PessimisticLockException if the refreshed version is not equal to the previous version which the entity had prior to the pessimistic refresh. h4. $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). h4. $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. |
| Assignee | Roman S. Borschel [ romanb ] | Benjamin Eberlei [ beberlei ] |
| 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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec h4. $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. h4. $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. h4. $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. h4. $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. h4. $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 h4. $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) h4. $em->refresh($entity, LockMode::PESSIMISTIC_READ/WRITE) Effects: * Throw TransactionRequiredException if there is no active transaction. * Throw PessimisticLockException if entity is not versioned. refresh() with a pessimistic lock is a "lock after read" so there might be stale data. Entity must be versioned in order to verify that. Alternatively we could compare *all* columns but thats going too far imho. * 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. * Throw PessimisticLockException if the refreshed version is not equal to the previous version which the entity had prior to the pessimistic refresh. h4. $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). h4. $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. |
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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec h4. $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. h4. $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. h4. $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. h4. $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. h4. $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 h4. $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) h4. $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. h4. $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). h4. $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. |
| 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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec h4. $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. h4. $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. h4. $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. h4. $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. h4. $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 h4. $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) h4. $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. h4. $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). h4. $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. |
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: {code} final abstract class Lock { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} 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: {code} public function find($class, $identifier, $lockMode=0, $lockVersion=null); {code} And if the values are set, set the appropriate query hints. ---- h2. Updated API specification h3. LockModes {code} final abstract class LockMode { const NONE = 0; const OPTIMISTIC = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } {code} h3. 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. h3. API spec h4. $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. h4. $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. h4. $em->find($entity, LockMode::OPTIMISTIC) (or findBy et al) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise proceeed normally. h4. $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. h4. $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 h4. $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) h4. $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. h4. $em->lock($entity, LockMode::OPTIMISTIC) Effects: * Throw OptimisticLockException if entity is not versioned. * Otherwise do nothing (NOOP). h4. $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. |
| Attachment | ddc178_locking.diff [ 10542 ] |
| Fix Version/s | 2.0-BETA3 [ 10060 ] | |
| Fix Version/s | 2.0-BETA2 [ 10050 ] |
| Status | Open [ 1 ] | Resolved [ 5 ] |
| Resolution | Fixed [ 1 ] |
| Workflow | jira [ 10518 ] | jira-feedback [ 14187 ] |
| Workflow | jira-feedback [ 14187 ] | jira-feedback2 [ 16051 ] |
| Workflow | jira-feedback2 [ 16051 ] | jira-feedback3 [ 18304 ] |
- Request to http://www.doctrine-project.org/fisheye/ failed: Error in remote call to 'FishEye 0 (http://www.doctrine-project.org/fisheye/)' (http://www.doctrine-project.org/fisheye) [AbstractRestCommand{path='/rest-service-fe/search-v1/crossRepositoryQuery', params={query=DDC-178, expand=changesets[-21:-1].revisions[0:29],reviews}, methodType=GET}] : Received status code 503 (Service Temporarily Unavailable)
We should be aware that PHP 5.3 now uses mysqlnd driver internally.
This means that queries like LOCK are applied via unbuffered queries, which may compromise subsequent data changes on DB table.
We need to do some testing before effectively apply any type of approach here.