Optimisation du refresh cache avec batch processing
Problème : Le refresh cache de tous les projets timeout côté serveur à cause du trop grand nombre de projets à traiter en une seule requête. Solution : Batch processing avec indicateur de progression - Backend : traite 10 projets par batch avec offset/limit - Frontend : fait plusieurs requêtes successives et affiche la progression - Timeout réduit à 60s par batch au lieu de illimité - Bouton désactivé pendant le traitement - Ajout invalidateNotificationsCache() pour vider aussi ce cache Affichage : "15/50 (30%)" pendant le traitement, puis "Terminé (50)" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
86db1f5a0c
commit
a57b0c203a
2 changed files with 119 additions and 20 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
:icon="icon"
|
:icon="icon"
|
||||||
:title="title"
|
:title="title"
|
||||||
@click="refreshCache()"
|
@click="refreshCache()"
|
||||||
|
:disabled="isProcessing"
|
||||||
>{{ text }}</k-button
|
>{{ text }}</k-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -24,6 +25,8 @@ const { pageUri, pageStatus, lastCacheUpdate } = defineProps({
|
||||||
const text = ref("Rafraîchir");
|
const text = ref("Rafraîchir");
|
||||||
const icon = ref("refresh");
|
const icon = ref("refresh");
|
||||||
const theme = ref("aqua-icon");
|
const theme = ref("aqua-icon");
|
||||||
|
const isProcessing = ref(false);
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
return lastCacheUpdate?.length > 0
|
return lastCacheUpdate?.length > 0
|
||||||
? "Dernière mise à jour : " + lastCacheUpdate
|
? "Dernière mise à jour : " + lastCacheUpdate
|
||||||
|
|
@ -31,25 +34,89 @@ const title = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function refreshCache() {
|
async function refreshCache() {
|
||||||
text.value = "En cours…";
|
isProcessing.value = true;
|
||||||
icon.value = "loader";
|
icon.value = "loader";
|
||||||
theme.value = "orange-icon";
|
theme.value = "orange-icon";
|
||||||
|
|
||||||
|
// Pour les projets multiples (batch processing)
|
||||||
|
if (pageUri === 'projects') {
|
||||||
|
await refreshAllProjects();
|
||||||
|
} else {
|
||||||
|
await refreshSingleProject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshAllProjects() {
|
||||||
|
let offset = 0;
|
||||||
|
const limit = 10; // 10 projets par batch
|
||||||
|
let hasMore = true;
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (hasMore) {
|
||||||
|
const init = {
|
||||||
|
method: "POST",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
body: JSON.stringify({
|
||||||
|
pageUri: 'projects',
|
||||||
|
offset,
|
||||||
|
limit
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await fetch("/refresh-cache.json", init);
|
||||||
|
const json = await res.json();
|
||||||
|
|
||||||
|
if (json.status === "error") {
|
||||||
|
throw new Error(json.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
total = json.total;
|
||||||
|
hasMore = json.hasMore;
|
||||||
|
offset = json.nextOffset;
|
||||||
|
|
||||||
|
// Mise à jour de la progression
|
||||||
|
const progress = Math.round((json.processed / json.total) * 100);
|
||||||
|
text.value = `${json.processed}/${json.total} (${progress}%)`;
|
||||||
|
|
||||||
|
console.log(`Batch terminé : ${json.processed}/${json.total} projets`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Succès
|
||||||
|
text.value = `Terminé (${total})`;
|
||||||
|
icon.value = "check";
|
||||||
|
theme.value = "green-icon";
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
location.href = location.href;
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
text.value = "Erreur";
|
||||||
|
icon.value = "alert";
|
||||||
|
theme.value = "red-icon";
|
||||||
|
isProcessing.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshSingleProject() {
|
||||||
|
text.value = "En cours…";
|
||||||
|
|
||||||
const init = {
|
const init = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
body: JSON.stringify({ pageUri }),
|
body: JSON.stringify({ pageUri }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetch("/refresh-cache.json", init);
|
try {
|
||||||
const json = await res.json();
|
const res = await fetch("/refresh-cache.json", init);
|
||||||
|
const json = await res.json();
|
||||||
|
|
||||||
|
if (json.status === "error") {
|
||||||
|
throw new Error(json.message);
|
||||||
|
}
|
||||||
|
|
||||||
if (json.status === "error") {
|
|
||||||
console.error(json);
|
|
||||||
text.value = "Erreur";
|
|
||||||
icon.value = "alert";
|
|
||||||
theme.value = "red-icon";
|
|
||||||
} else {
|
|
||||||
console.log(json);
|
console.log(json);
|
||||||
text.value = "Terminé";
|
text.value = "Terminé";
|
||||||
icon.value = "check";
|
icon.value = "check";
|
||||||
|
|
@ -58,6 +125,13 @@ async function refreshCache() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
location.href = location.href;
|
location.href = location.href;
|
||||||
}, 1500);
|
}, 1500);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
text.value = "Erreur";
|
||||||
|
icon.value = "alert";
|
||||||
|
theme.value = "red-icon";
|
||||||
|
isProcessing.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
set_time_limit(0);
|
set_time_limit(60);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'pattern' => '/refresh-cache.json',
|
'pattern' => '/refresh-cache.json',
|
||||||
|
|
@ -10,17 +10,42 @@ return [
|
||||||
|
|
||||||
if ($data->pageUri === 'projects') {
|
if ($data->pageUri === 'projects') {
|
||||||
$projects = page('projects')->children();
|
$projects = page('projects')->children();
|
||||||
foreach ($projects as $project) {
|
|
||||||
$project->rebuildStepsCache();
|
|
||||||
|
|
||||||
$formatter = new IntlDateFormatter('fr_FR', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, 'Europe/Paris');
|
// Support du batch processing
|
||||||
$project->update([
|
$offset = isset($data->offset) ? intval($data->offset) : 0;
|
||||||
'lastCacheUpdate' => $formatter->format(time())
|
$limit = isset($data->limit) ? intval($data->limit) : 10; // 10 projets par batch par défaut
|
||||||
]);
|
$total = $projects->count();
|
||||||
|
|
||||||
|
// Slice pour ne traiter qu'un batch
|
||||||
|
$batch = $projects->slice($offset, $limit);
|
||||||
|
$processed = 0;
|
||||||
|
|
||||||
|
foreach ($batch as $project) {
|
||||||
|
try {
|
||||||
|
$project->rebuildStepsCache();
|
||||||
|
$project->invalidateNotificationsCache();
|
||||||
|
|
||||||
|
$formatter = new IntlDateFormatter('fr_FR', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, 'Europe/Paris');
|
||||||
|
$project->update([
|
||||||
|
'lastCacheUpdate' => $formatter->format(time())
|
||||||
|
]);
|
||||||
|
$processed++;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
error_log("Error refreshing cache for project {$project->slug()}: " . $e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$remaining = max(0, $total - ($offset + $processed));
|
||||||
|
$hasMore = $remaining > 0;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'satus' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Données des pages projets rafraîchies avec succès.'
|
'message' => "Batch terminé : $processed projets traités.",
|
||||||
|
'processed' => $offset + $processed,
|
||||||
|
'total' => $total,
|
||||||
|
'remaining' => $remaining,
|
||||||
|
'hasMore' => $hasMore,
|
||||||
|
'nextOffset' => $hasMore ? $offset + $limit : null
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|
@ -41,7 +66,7 @@ return [
|
||||||
|
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
return [
|
return [
|
||||||
'satus' => 'error',
|
'status' => 'error',
|
||||||
'message' => 'Impossible de rafraîchir les données de la page ' . $data->pageUri . '. Aucun projet correspondant.'
|
'message' => 'Impossible de rafraîchir les données de la page ' . $data->pageUri . '. Aucun projet correspondant.'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +80,7 @@ return [
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'satus' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Données de la page ' . $data->pageUri . ' rafraîchie avec succès.'
|
'message' => 'Données de la page ' . $data->pageUri . ' rafraîchie avec succès.'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue