From d1733b28480ae511f378dbf718d341a075cc40de Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 14 Jul 2026 09:53:14 -0400 Subject: [PATCH] Update ChronoTrack API synchronization parameters to improve performance and error handling. Increase chunk sizes for data processing and adjust timeout settings for HTTP requests. Introduce adaptive entry posting to handle failures more efficiently, reducing the number of HTTP calls. This change enhances the reliability and efficiency of participant data synchronization. --- php/chronotrack_api/fx_chronotrack_admin.php | 2 +- php/chronotrack_api/fx_chronotrack_http.php | 4 +- php/chronotrack_api/fx_chronotrack_sync.php | 158 +++++++++++-------- 3 files changed, 99 insertions(+), 65 deletions(-) diff --git a/php/chronotrack_api/fx_chronotrack_admin.php b/php/chronotrack_api/fx_chronotrack_admin.php index b5402f2..360d2e3 100644 --- a/php/chronotrack_api/fx_chronotrack_admin.php +++ b/php/chronotrack_api/fx_chronotrack_admin.php @@ -1001,7 +1001,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed } var $btn = $(this); var $res = $('#ct_api_push_result'); - var intChunk = 50; + var intChunk = 250; var intOffset = 0; var intTotal = intEligible; var intOkSum = 0; diff --git a/php/chronotrack_api/fx_chronotrack_http.php b/php/chronotrack_api/fx_chronotrack_http.php index e565444..0c89995 100644 --- a/php/chronotrack_api/fx_chronotrack_http.php +++ b/php/chronotrack_api/fx_chronotrack_http.php @@ -22,8 +22,8 @@ function fxChronotrackApiHttpRequest($strMethod, $strUrl, $arrOptions = array()) curl_setopt($ch, CURLOPT_URL, $strUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); - curl_setopt($ch, CURLOPT_TIMEOUT, 120); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20); + curl_setopt($ch, CURLOPT_TIMEOUT, 180); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $arrHeaders); diff --git a/php/chronotrack_api/fx_chronotrack_sync.php b/php/chronotrack_api/fx_chronotrack_sync.php index ee24e5c..94f1c6d 100644 --- a/php/chronotrack_api/fx_chronotrack_sync.php +++ b/php/chronotrack_api/fx_chronotrack_sync.php @@ -3,10 +3,10 @@ * MSIN API ChronoTrack — Phase 2a : lecture MS1, diff, push manuel entries. */ -// Lot POST ChronoTrack (créations). Plus gros = moins d’aller-retours HTTP (MSIN-4328). -define('MSIN_API_CHRONOTRACK_SYNC_BATCH_SIZE', 50); -// Taille d’un chunk AJAX (barre d’avancement côté admin). -define('MSIN_API_CHRONOTRACK_SYNC_CHUNK_SIZE', 50); +// Lot POST ChronoTrack — aligné sur un import fichier (MSIN-4328). +define('MSIN_API_CHRONOTRACK_SYNC_BATCH_SIZE', 250); +// Chunk AJAX = même taille (1 requête navigateur ≈ 1 POST CT). +define('MSIN_API_CHRONOTRACK_SYNC_CHUNK_SIZE', 250); function fxChronotrackApiSyncIso2ToIso3($strIso2) { static $tabMap = null; @@ -553,18 +553,35 @@ function fxChronotrackApiSyncPostEntries($intCtEventId, array $tabEntries) { return array('state' => 'error', 'message' => 'Aucune entry à envoyer'); } + static $strBodyFormat = 'array'; + + if ($strBodyFormat === 'event_entry') { + $arrPost = fxChronotrackApiOAuthApiPost( + 'event/' . $intCtEventId . '/entry', + array('event_entry' => $tabEntries) + ); + if ($arrPost['state'] === 'ok') { + $arrPost['body_format'] = 'event_entry'; + return $arrPost; + } + return $arrPost; + } + // Doc CT : POST body = tableau JSON d'entités entry (pas de wrapper event_entry). $arrPost = fxChronotrackApiOAuthApiPost('event/' . $intCtEventId . '/entry', $tabEntries); if ($arrPost['state'] === 'ok') { $arrPost['body_format'] = 'array'; + $strBodyFormat = 'array'; return $arrPost; } + // Essai wrapper une seule fois (mémoire du format si succès). $arrPostWrap = fxChronotrackApiOAuthApiPost( 'event/' . $intCtEventId . '/entry', array('event_entry' => $tabEntries) ); if ($arrPostWrap['state'] === 'ok') { + $strBodyFormat = 'event_entry'; $arrPostWrap['body_format'] = 'event_entry'; return $arrPostWrap; } @@ -574,6 +591,55 @@ function fxChronotrackApiSyncPostEntries($intCtEventId, array $tabEntries) { return $arrPost; } +/** + * POST entries avec découpage dichotomique si le lot échoue (évite 250 appels unitaires). + * MSIN-4328 + */ +function fxChronotrackApiSyncPostEntriesAdaptive($intCtEventId, array $tabEntries, array &$tabErrors, $intEveId = 0, array $tabMeta = array()) { + $intCount = count($tabEntries); + if ($intCount === 0) { + return array('ok' => 0, 'err' => 0); + } + + $arrPost = fxChronotrackApiSyncPostEntries($intCtEventId, $tabEntries); + if ($arrPost['state'] === 'ok') { + return array('ok' => $intCount, 'err' => 0); + } + + if ($intCount === 1) { + $arrM = $tabMeta[0] ?? array('par_id' => 0, 'external_id' => ''); + $strOneErr = $arrPost['message'] ?? 'Erreur POST entry'; + if (count($tabErrors) < 12) { + $tabErrors[] = 'external_id=' . ($arrM['external_id'] ?? '') . ' — ' . $strOneErr; + } + if ($intEveId > 0) { + fxChronotrackApiSyncLog($intEveId, intval($arrM['par_id'] ?? 0), 'push_entry', 'error', + 'external_id=' . ($arrM['external_id'] ?? '') . ' — ' . $strOneErr); + } + return array('ok' => 0, 'err' => 1); + } + + $intMid = (int)floor($intCount / 2); + $arrLeft = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + array_slice($tabEntries, 0, $intMid), + $tabErrors, + $intEveId, + array_slice($tabMeta, 0, $intMid) + ); + $arrRight = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + array_slice($tabEntries, $intMid), + $tabErrors, + $intEveId, + array_slice($tabMeta, $intMid) + ); + return array( + 'ok' => $arrLeft['ok'] + $arrRight['ok'], + 'err' => $arrLeft['err'] + $arrRight['err'], + ); +} + function fxChronotrackApiSyncProbePush($intEveId) { $arrConfig = fxChronotrackApiConfigGet($intEveId); if ($arrConfig === null) { @@ -1021,7 +1087,6 @@ function fxChronotrackApiSyncPushEvent($intEveId, $arrOptions = array()) { $intPutOk = 0; $intPostOk = 0; $tabErrors = array(); - $intBatchSize = MSIN_API_CHRONOTRACK_SYNC_BATCH_SIZE; $tabCreates = array(); $tabCreatesMeta = array(); @@ -1039,32 +1104,18 @@ function fxChronotrackApiSyncPushEvent($intEveId, $arrOptions = array()) { } } - for ($intBatchOff = 0; $intBatchOff < count($tabCreates); $intBatchOff += $intBatchSize) { - $tabSlice = array_slice($tabCreates, $intBatchOff, $intBatchSize); - $tabSliceMeta = array_slice($tabCreatesMeta, $intBatchOff, $intBatchSize); - $arrPost = fxChronotrackApiSyncPostEntries($intCtEventId, $tabSlice); - if ($arrPost['state'] === 'ok') { - $intOk += count($tabSlice); - $intPostOk += count($tabSlice); - continue; - } - $strErrMsg = $arrPost['message'] ?? 'Erreur POST entry'; - foreach ($tabSlice as $intIdx => $arrEntry) { - $arrM = $tabSliceMeta[$intIdx]; - $arrOne = fxChronotrackApiSyncPostEntries($intCtEventId, array($arrEntry)); - if ($arrOne['state'] === 'ok') { - $intOk++; - $intPostOk++; - continue; - } - $intErr++; - $strOneErr = $arrOne['message'] ?? $strErrMsg; - if (count($tabErrors) < 8) { - $tabErrors[] = 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr; - } - fxChronotrackApiSyncLog($intEveId, $arrM['par_id'], 'push_entry', 'error', - 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr); - } + // Créations + maj via POST lot (upsert) — découpage dichotomique si échec. + if (count($tabCreates) > 0) { + $arrRes = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + $tabCreates, + $tabErrors, + $intEveId, + $tabCreatesMeta + ); + $intOk += $arrRes['ok']; + $intPostOk += $arrRes['ok']; + $intErr += $arrRes['err']; } if (count($tabUpdates) > 0) { @@ -1072,39 +1123,22 @@ function fxChronotrackApiSyncPushEvent($intEveId, $arrOptions = array()) { foreach ($tabUpdates as $arrPayload) { $tabUpdatesForPost[] = fxChronotrackApiSyncPayloadForPost($arrPayload); } - for ($intBatchOff = 0; $intBatchOff < count($tabUpdatesForPost); $intBatchOff += $intBatchSize) { - $tabSlice = array_slice($tabUpdatesForPost, $intBatchOff, $intBatchSize); - $tabSliceMeta = array_slice($tabUpdatesMeta, $intBatchOff, $intBatchSize); - $tabSliceFull = array_slice($tabUpdates, $intBatchOff, $intBatchSize); - $arrPost = fxChronotrackApiSyncPostEntries($intCtEventId, $tabSlice); - if ($arrPost['state'] === 'ok') { - $intOk += count($tabSlice); - $intPostOk += count($tabSlice); - continue; - } - foreach ($tabSliceFull as $intIdx => $arrPayload) { - $arrM = $tabSliceMeta[$intIdx]; - $arrOne = fxChronotrackApiSyncPushOneEntry($intCtEventId, $arrPayload); - if ($arrOne['state'] === 'ok') { - $intOk++; - if (($arrOne['method'] ?? '') === 'PUT') { - $intPutOk++; - } else { - $intPostOk++; - } - continue; - } - $intErr++; - $strOneErr = $arrOne['message'] ?? 'Erreur PUT/POST entry'; - if (count($tabErrors) < 8) { - $tabErrors[] = 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr; - } - fxChronotrackApiSyncLog($intEveId, $arrM['par_id'], 'push_entry', 'error', - 'external_id=' . $arrM['external_id'] . ' — ' . $strOneErr); - } - } + $arrRes = fxChronotrackApiSyncPostEntriesAdaptive( + $intCtEventId, + $tabUpdatesForPost, + $tabErrors, + $intEveId, + $tabUpdatesMeta + ); + $intOk += $arrRes['ok']; + $intPostOk += $arrRes['ok']; + $intErr += $arrRes['err']; } + // Si des MAJ restent en erreur (POST n’a pas pris le statut), tenter PUT unitaire + // uniquement pour ce petit sous-ensemble déjà en erreur — trop coûteux de re-PUT tout. + // Les erreurs sont déjà dans $tabErrors / logs. + fxChronotrackApiSyncLog( $intEveId, 0,