@@ -23,6 +23,7 @@ class CodeImporter
2323 private ?string $ currentLockName = null ;
2424 private int $ lockRetryAttempts = 10 ;
2525 private int $ lockRetryDelaySeconds = 30 ;
26+ private bool $ waitedForLock = false ;
2627
2728 /**
2829 * Set custom temporary directory
@@ -102,6 +103,12 @@ public function import(string $codeType, bool $isWindows = false, bool $usExtens
102103 $ this ->acquireLock ($ codeType );
103104
104105 try {
106+ // If we waited for the lock, check if vocabulary was already imported
107+ if ($ this ->waitedForLock && $ this ->isVocabularyLoaded ($ codeType )) {
108+ echo "Vocabulary {$ codeType } was already imported by another process. Skipping import. \n" ;
109+ return ;
110+ }
111+
105112 switch ($ codeType ) {
106113 case 'RXNORM ' :
107114 $ this ->importRxnorm ($ isWindows );
@@ -228,6 +235,48 @@ private function importValueset(string $type): void
228235 }
229236 }
230237
238+ /**
239+ * Check if vocabulary is already loaded by examining standardized_tables_track
240+ */
241+ public function isAlreadyLoaded (string $ codeType , string $ revisionDate , string $ version , string $ fileChecksum ): bool
242+ {
243+ if (!function_exists ('sqlQuery ' )) {
244+ return false ;
245+ }
246+
247+ $ result = sqlQuery (
248+ "SELECT COUNT(*) as count FROM `standardized_tables_track` WHERE `name` = ? AND `revision_date` = ? AND `revision_version` = ? AND `file_checksum` = ? " ,
249+ array ($ codeType , $ revisionDate , $ version , $ fileChecksum )
250+ );
251+
252+ return $ result && $ result ['count ' ] > 0 ;
253+ }
254+
255+ /**
256+ * Check if any version of vocabulary is loaded (simpler check for post-lock validation)
257+ */
258+ public function isVocabularyLoaded (string $ codeType ): bool
259+ {
260+ if (!function_exists ('sqlQuery ' )) {
261+ return false ;
262+ }
263+
264+ // Use SNOMED for tracking regardless of RF2 format to match OpenEMR expectations
265+ $ trackingCodeType = ($ codeType === 'SNOMED_RF2 ' ) ? 'SNOMED ' : $ codeType ;
266+
267+ try {
268+ $ result = sqlQuery (
269+ "SELECT COUNT(*) as count FROM standardized_tables_track WHERE name = ? " ,
270+ [$ trackingCodeType ]
271+ );
272+
273+ return $ result && $ result ['count ' ] > 0 ;
274+ } catch (\Exception $ e ) {
275+ // If query fails, assume not loaded to be safe
276+ return false ;
277+ }
278+ }
279+
231280 /**
232281 * Update tracking table
233282 */
@@ -293,6 +342,7 @@ private function acquireLock(string $codeType): void
293342
294343 if ($ attempt < $ this ->lockRetryAttempts ) {
295344 echo "Lock is held by another process. Waiting {$ delay } seconds before retry {$ attempt }/ {$ this ->lockRetryAttempts }... \n" ;
345+ $ this ->waitedForLock = true ;
296346 sleep ($ delay );
297347
298348 // Exponential backoff with jitter (cap at 5 minutes)
@@ -349,6 +399,7 @@ private function releaseLock(): void
349399 error_log ("Warning: Failed to release database lock ' {$ this ->currentLockName }': " . $ e ->getMessage ());
350400 } finally {
351401 $ this ->currentLockName = null ;
402+ $ this ->waitedForLock = false ;
352403 }
353404 }
354405
0 commit comments