doctrine/DoctrineMongoDBBundle

mkdir() race condition in cache warmers

andrey-tech opened this issue · 1 comments

DoctrineMongoDBBundle version(s) affected

3.5.3, up to 4.6.x

Description

The race condition is appears when several processes are attempting to create a same Doctrine Proxy/Hydrator directory which does not yet exist and \RuntimeException is thrown:

  1. \Doctrine\Bundle\MongoDBBundle\CacheWarmer\ProxyCacheWarmer
50        if (!file_exists($proxyCacheDir)) {
51            if (false === @mkdir($proxyCacheDir, 0775, true)) {
52                throw new \RuntimeException(sprintf('Unable to create the Doctrine Proxy directory (%s)', dirname($proxyCacheDir)));
53            }
  1. \Doctrine\Bundle\MongoDBBundle\CacheWarmer\PersistentCollectionCacheWarmer
47        if (! file_exists($collCacheDir)) {
48           if (false === @mkdir($collCacheDir, 0775, true)) {
49                throw new \RuntimeException(sprintf('Unable to create the Doctrine persistent collection directory (%s)', dirname($collCacheDir)));
50            }
  1. \Doctrine\Bundle\MongoDBBundle\CacheWarmer\HydratorCacheWarmer
48        if (!file_exists($hydratorCacheDir)) {
49            if (false === @mkdir($hydratorCacheDir, 0775, true)) {
50                throw new \RuntimeException(sprintf('Unable to create the Doctrine Hydrator directory (%s)', dirname($hydratorCacheDir)));
51            }
  1. \Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\CreateProxyDirectoryPass
22        if (!is_dir($proxyCacheDir)) {
23            if (false === @mkdir($proxyCacheDir, 0775, true)) {
24               exit(sprintf('Unable to create the Doctrine Proxy directory (%s)', dirname($proxyCacheDir)));
25            }
  1. \Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\CreateHydratorDirectoryPass
22        if (!is_dir($hydratorCacheDir)) {
23            if (false === @mkdir($hydratorCacheDir, 0775, true)) {
24                exit(sprintf('Unable to create the Doctrine Hydrator directory (%s)', dirname($hydratorCacheDir)));
25            }

How to reproduce

This issue is difficult to reproduce, as any concurrency-related issues are. It appears when several processes are attempting to create a directory which does not yet exist. Specifically, when one process is between !file_exists() and @mkdir() after another process has already managed to create the directory.

Possible Solution

Add an extra check !file_exists(). Example:

  1. \Doctrine\Bundle\MongoDBBundle\CacheWarmer\ProxyCacheWarmer
50        if (!file_exists($proxyCacheDir)) {
51            if (false === @mkdir($proxyCacheDir, 0775, true) && !file_exists($proxyCacheDir)) {
52                throw new \RuntimeException(sprintf('Unable to create the Doctrine Proxy directory (%s)', dirname($proxyCacheDir)));
53            }

Additional Context

Same problem in symfony/symfony already was fixed - see issue #47489.

Closing as #747 was merged