foundry icon indicating copy to clipboard operation
foundry copied to clipboard

Random "Unique violation ERROR" while creating fixtures with faker::uniq

Open VincentLanglet opened this issue 1 month ago • 3 comments

Hi @nikophil, I'm getting random failure from my DB while generating my fixtures and I'm wondering if I'm doing something wrong.

For instance, the last error I got was with the following code

$translationSource1 = TranslationSourceFactory::createOne([
     'project' => $project,
]);
TranslationTargetFactory::createOne([
     'project' => $project,
     'translationSource' => $translationSource1,
]);
TranslationTargetFactory::createOne([
     'project' => $project,
     'translationSource' => $translationSource1,
]);

which gave me a failure

Doctrine\DBAL\Exception\UniqueConstraintViolationException: An exception occurred while executing a query: SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint "languages_pkey"
DETAIL:  Key (code)=(code_mr) already exists.

TranslationLabelFactory has

protected function defaults(): array
    {
        return [
            'project' => ProjectFactory::new(),
            'translationSource' => TranslationSourceFactory::new(),
            'languagePair' => LanguagePairFactory::new(),
        ];
    }

LanguagePairFactory has

protected function defaults(): array
    {
        return [
            'source' => LanguageFactory::new(),
            'target' => LanguageFactory::new(),
        ];
    }

and LanguageFactory has

protected function defaults(): array
    {
        return [
            'code' => 'code_'.self::faker()->unique()->languageCode(),
            'name' => self::faker()->unique()->text(10),
        ];
    }

By using self::faker()->unique() I expect an unique value every time I create a new fixtures, do I misuse/misunderstand something ?

Edit: also, I feel like the faker instance is shared across all the factories (since it's attached to the Configuration instance), would it be possible to have a faker instance by factory instead ? In my use case I want the value to be unique for a specific factory but two different factories can use the same value.

VincentLanglet avatar Nov 19 '25 17:11 VincentLanglet

I dunno if it could help but I'm getting almost every time a random failure since I bumped from 2.7.2 to 2.8.0.

Reverting to 2.7.2 seems to works so it seems like there is some new unwanted behavior...

Edit: This comment might be irrelevant, since bumping again to 2.8.0 seems to work now. Those failure are really random...

VincentLanglet avatar Nov 19 '25 19:11 VincentLanglet

Hi @VincentLanglet

any chance you provide a reproducer? even if it does not fail on each test run

This problem could occur if faker()->seed() (or even mt_srand()) is called twice in the same test

nikophil avatar Nov 20 '25 06:11 nikophil

Hi @VincentLanglet

any chance you provide a reproducer? even if it does not fail on each test run

Not the easier thing to do since the project is private... I'll see if it really needed.

This problem could occur if faker()->seed() (or even mt_srand()) is called twice in the same test

I have the config

when@dev: &dev
    # See full configuration: https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#full-default-bundle-configuration
    zenstruck_foundry:
        enable_auto_refresh_with_lazy_objects: true
        persistence:
            # Flush only once per call of `PersistentObjectFactory::create()`
            flush_once: true

    services:
        _defaults:
            autowire: true
            autoconfigure: true

        Engine\Tests\Factory\:
            resource: '%kernel.project_dir%/tests/Factory/'

when@test: *dev

and in my code I never "manually" touch to faker or mt_srand, my usage feel natural to me

BUT, I just discovered a mistake on my side, while bumping the version I added the code

protected function initialize(): static
    {
        return $this
            ->afterPersist(function (Project $project): bool {
                if ($project->hasSetting()) {
                    return false;
                }
                
                $project->setSetting(ProjectSettingFactory::createOne(['project' => $this])); // HERE

                return true;
            });
    }

I called ProjectSettingFactory::createOne(['project' => $this]) instead of ProjectSettingFactory::createOne(['project' => $project]) and it seems that "changing" this solve my issue. Sorry for the wrong report...

To me it's weird cause:

  • it seems that fixtures were created "correctly" without error
  • it's not easy to see (to me) the link between this mistake and the error I got
  • (Also was hard to debug cause the failure only happen on ci, and I still don't get why I cannot have it locally...)

But maybe you'll see the reason (and if there is something to improve on foundry side to avoid this error or improve the error message ?)

VincentLanglet avatar Nov 20 '25 07:11 VincentLanglet