Skip to content

Commit a8f1fb4

Browse files
committed
feat(concurrency): recheck if vocab is loaded after waiting for lock
1 parent 786166b commit a8f1fb4

File tree

2 files changed

+52
-14
lines changed

2 files changed

+52
-14
lines changed

src/Command/ImportCodesCommand.php

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
181181
$trackingCodeType = ($codeType === 'SNOMED_RF2') ? 'SNOMED' : $codeType;
182182
$fileChecksum = $metadata['checksum'] ?: md5_file($filePath);
183183

184-
if ($this->isAlreadyLoaded($trackingCodeType, $metadata['revision_date'], $metadata['version'], $fileChecksum)) {
184+
if ($this->importer->isAlreadyLoaded($trackingCodeType, $metadata['revision_date'], $metadata['version'], $fileChecksum)) {
185185
$io->warning("Code package appears to be already loaded (same type, version, revision date, and file checksum)");
186186
$io->note("Use --force flag to import anyway, or --dry-run to test without checking");
187187
return Command::SUCCESS;
@@ -293,18 +293,5 @@ private function is_absolute_path(string $path): bool
293293
/**
294294
* Check if a code package is already loaded with the same metadata
295295
*/
296-
private function isAlreadyLoaded(string $codeType, string $revisionDate, string $version, string $fileChecksum): bool
297-
{
298-
if (!function_exists('sqlQuery')) {
299-
return false;
300-
}
301-
302-
$result = sqlQuery(
303-
"SELECT COUNT(*) as count FROM `standardized_tables_track` WHERE `name` = ? AND `revision_date` = ? AND `revision_version` = ? AND `file_checksum` = ?",
304-
array($codeType, $revisionDate, $version, $fileChecksum)
305-
);
306-
307-
return $result && $result['count'] > 0;
308-
}
309296

310297
}

src/Service/CodeImporter.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)