DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Practical PHP Patterns: Coarse Grained Lock

Practical PHP Patterns: Coarse Grained Lock

Giorgio Sironi user avatar by
Giorgio Sironi
·
Aug. 22, 10 · Interview
Like (0)
Save
Tweet
Share
1.09K Views

Join the DZone community and get the full member experience.

Join For Free

The Optimistic and Pessimistic locks are basic patterns, which can in turn be composed with other ones. The Coarse Grained Lock pattern is a combination of the Aggregate pattern and of a lock-related one. The Coarse Grained Lock can be used without implementing Aggregate in an object model, but it works very well in conjunction with it, since it is a very efficient way of defining what are the subsets of the graph to lock at the same time.

The base assumption of this pattern is that there is a mismatch between the single objects of the application-wide graph and the actual Aggregate you're going to base your transactions on. Small classes and objects are ideal for domain modelling, while the necessity to lock every single object involved would put an unspoken constraint over the freedom of modelling to keep the system usable. It is not always practical to apply a lock pattern on single objects, thus this pattern solves the issue by defining a lock (optimistic or pessimistic) over a whole Aggregate.

An Aggregate refresh

The definition of an Aggregate is that of a subgraph used for breaking dependencies between the various objects. Defining Aggregates aids the graph in becoming loosely connected: the subgraph is the Aggregate, while the Facade object which encapsulates its internals is called the Aggregate Root.

The technical rule for implementing Aggregates is that no persistent references can be allowed (the ones save in the database, with foreign keys or similar mechanisms) to objects that are inside an aggregate. Only references to the root are permitted, while transient references can be obtained for the internal objects. It make sense to put in an Aggregate objects which are frequently used together and validated together.

For example, a class User which composes two Phonenumber and Address classes is probably the ideal target for constituting an Aggregate. In this example, I can call getAddresses() to obtain the Address objects and print them, but this reference is transient (destroyed at the end of the script): according to the domain model, I can't save it a a field of some other domain object. The User aggregate root should manage the lifecycle of all the internal objects.

As a side note, remember that nothing implies that transient references should be handed out by their Aggregate Roots: it would be ideal for them to be totally encapsulated and never exit the Aggregate's code, in order for the internals to change without the Aggregate's client code noticing.

Implementation

When an Aggregate is needed in a business transaction (as a whole) you lock the entire Aggregate, since we can assume that if the cohesion of the Aggregate is ideal, a Coarse Grained Lock does not result in unnecessary contention of the other near objects. Continuing with our example, it would make little sense for some User to edit his Address list while an administrator changes the User's Phonenumber.

Usually, when an optimistic lock is the chosen lock pattern, all the objects in the Aggregate share the same version of the root; the root is associated with the lock, and hides the various composed objects as well as the version field. When a change is applied to even only one of the internal objects, the version number is incremented, as if the Aggregate were a single, large object.

An alternative in this implementation is a shared lock, when the group of objects is not strictly an Aggregate and they share a common version object. However Aggregate boundaries and locked groups often coincide, thus it is reasonable to take advantage of the common ground between them.

The Pessimistic Lock version of this pattern is similar, since it focuses only on root objects as well.

These patterns are usually implemented while using an Object-Relational Mapper, there are some infrastructure choices to make along the way. For example the storage strategy for an ORM can be:

  • in case of a shared lock, a version table, which is joined with the other objects' ones via a classic foreign key;
  • in case of a root lock, a version column on the root object's table.

As always, the business domain and its requirements govern the definition of both Aggregates and locking groups.

The process of using a Coarse Grained Locks is similar to the following:

  • the entire Aggregate is loaded, with its version field (assuming a root lock). Its data is modified by the user.
  • When the entire Aggregate is saved, its version field has to be compared with the old one.
  • If the transaction is successful, and there are changes to any object, the version number is incremented.

Advantages

This pattern simplifies the locking process, since there are actually less objects to lock, and less version fields to clutter the various tables.

Disadvantages

The pattern should be used when it makes sense in the domain model. Create fictional relationships between objects to model them as Aggregates or to take advantage of a Coarse Grained Lock is not proficient in the long run.

Example

We'll see a code sample that implements the pattern over an example Product-ShippingDetails annotated for taking advantage of Doctrine 2 features. Product is the Aggregate Root and the starting code has been taken from the reference manual.

The version field is kept on the root, but an issue in implementing this pattern is that Doctrine does not detect changes on child objects. This mechanism has to be accounted for in userland code.

The hack used here is to add another version field, which is incremented when the Aggregate internal items are modified. This way, a change is detected in the root entity, and the real optimistic lock version field can be checked for consistency and incremented as well.

<?php

/** @Entity */
class Product
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
private $id;

/**
* @Column(type="integer")
* @Version
*/
private $version;

/**
* @Column(type="integer")
* @Version
*/
private $aggregateVersion;

/**
* @OneToOne(targetEntity="ShippingDetails")
* @JoinColumn(name="shipping_id", referencedColumnName="id")
*/
private $shipping;

public function setShipping(ShippingDetails $shipping)
{
$this->shipping = $shipping;
$this->aggregateVersion++;
}
}

/** @Entity */
class ShippingDetails
{
public function __construct($description)
{
$this->_description = $description;
}

public function __toString()
{
return $this->_description;
}
}

Feel free to add anything to the example, if you feel there is a cleaner way to implement Optimistic Lock at the Aggregate level with minimal changes to ordinary Optimistic Lock code.

Threading PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Front-End Troubleshooting Using OpenTelemetry
  • Understanding and Solving the AWS Lambda Cold Start Problem
  • The Power of Zero-Knowledge Proofs: Exploring the New ConsenSys zkEVM
  • Building the Next-Generation Data Lakehouse: 10X Performance

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: