Lets say we have some table
foo:
message: STRING
created: DATETIME
And we want to users may only add 1 row every 10 minutes.
$em = // \Doctrine\ORM\EntityManager
$repo = $em->getRepository('Foo');
$now = new \DateTime;
$tenMinutesAgo = $now->sub(new \DateInterval('PT10M'));
// See if there are any Foos in 10 minutes interval
// [SELECT]
$count = (int) $repo->createQueryBuilder('t')
->select('COUNT(t.id)')
->where('t.created > :ten_minutes_ago')
->setParameter('ten_minutes_ago', $tenMinutesAgo)
->getQuery()
->getSingleScalarResult();
// sleep(X) to simulate possible downtime
// [IF SELECT]
if (0 === $count) {
$foo = new Foo;
$foo->setMessage('bar')
->setCreated($now);
$em->persist($foo);
// [INSERT]
$em->flush();
echo "Added!";
} else {
echo "Cannot add, wait around 10 min.";
}
Now, 2 users (Alice and Bob) perfoming request at same time:
- Alice: Perfoming [SELECT] ... [IF SELECT] ok, count = 0
- Bob: Perfoming [SELECT] ... [IF SELECT] ok, count = 0
- Alice: [INSERT] ...
- Bob: [INSERT] ...
I think it's pretty common problem. How i can solve it? (with Doctrine, if it possible)
Solution 1. Table locks.
Do LOCK TABLE
before all queries, and release it while done.
- Actually, the example might be too simplified. ONE user cannot insert faster than 1 row at 10 min. With table locks all users have to wait, while another adds his row?
- Too sad, with Doctrine table locks might be very tricky. (DQL generates aliases, and we have to predict it in the native
LOCK TABLE
query.