55use App \Model \Entity \Group ;
66use App \Model \Entity \GroupExam ;
77use Doctrine \ORM \EntityManagerInterface ;
8+ use Doctrine \DBAL \Exception \UniqueConstraintViolationException ;
89use DateTime ;
910use Exception ;
1011
@@ -38,6 +39,36 @@ public function findPendingForGroup(Group $group): ?GroupExam
3839 return $ exam ? reset ($ exam ) : null ;
3940 }
4041
42+ /**
43+ * Internal helper that tries to find the exam or create it if not present.
44+ * It returns null if creation failed due to race condition (expects retry by caller).
45+ * @param Group $group
46+ * @param DateTime $begin
47+ * @param DateTime $end
48+ * @param bool $strict
49+ * @return GroupExam|null
50+ */
51+ private function tryFindOrCreate (Group $ group , DateTime $ begin , DateTime $ end , bool $ strict ): ?GroupExam
52+ {
53+ $ exam = $ this ->findBy (["group " => $ group , "begin " => $ begin ]);
54+ if (count ($ exam ) > 1 ) {
55+ throw new Exception ("Data corruption, there is more than one group exam starting at the same time. " );
56+ }
57+
58+ if (!$ exam ) {
59+ $ exam = new GroupExam ($ group , $ begin , $ end , $ strict );
60+ try {
61+ $ this ->persist ($ exam );
62+ } catch (UniqueConstraintViolationException ) {
63+ return null ;
64+ }
65+ } else {
66+ $ exam = reset ($ exam );
67+ }
68+
69+ return $ exam ;
70+ }
71+
4172 /**
4273 * Fetch group exam entity by group-begin index. If not present, new entity is created.
4374 * @param Group $group
@@ -56,18 +87,13 @@ public function findOrCreate(
5687 $ end = $ end ?? $ group ->getExamEnd ();
5788 $ strict = $ strict === null ? $ group ->isExamLockStrict () : $ strict ;
5889
59- $ exam = $ this ->findBy (["group " => $ group , "begin " => $ begin ]);
60- if (count ($ exam ) > 1 ) {
61- throw new Exception ("Data corruption, there is more than one group exam starting at the same time. " );
62- }
63-
64- if (!$ exam ) {
65- $ exam = new GroupExam ($ group , $ begin , $ end , $ strict );
66- $ this ->persist ($ exam );
67- } else {
68- $ exam = reset ($ exam );
90+ for ($ retries = 0 ; $ retries < 3 ; $ retries ++) {
91+ $ exam = $ this ->tryFindOrCreate ($ group , $ begin , $ end , $ strict );
92+ if ($ exam !== null ) {
93+ return $ exam ;
94+ }
6995 }
7096
71- return $ exam ;
97+ throw new Exception ( " Failed to find or create group exam after multiple attempts. " ) ;
7298 }
7399}
0 commit comments