Skip to content

Commit c83c337

Browse files
committed
refactor: pulizia file da controlli integrità per evitare timeout
1 parent 4170b4f commit c83c337

2 files changed

Lines changed: 220 additions & 94 deletions

File tree

modules/aggiornamenti/controlli.php

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1315,7 +1315,10 @@ function eseguiAzioneGlobale(buttonElement) {
13151315
// Determina i parametri da passare in base al tipo di controllo
13161316
let params = {};
13171317
if (controlloClass === "Modules\\\\Aggiornamenti\\\\Controlli\\\\IntegritaFile") {
1318-
params = {action: "remove_all_both"};
1318+
params = {
1319+
action: "remove_all_both",
1320+
limit: 100,
1321+
};
13191322
}
13201323
13211324
eseguiRisoluzioneGlobale(button, controlloId, controlloClass, function() {
@@ -1339,6 +1342,12 @@ function eseguiAzioneGlobale(buttonElement) {
13391342
function eseguiRisoluzioneGlobale(button, controlloId, controlloClass, successCallback, errorCallback, params = {}) {
13401343
let restore = buttonLoading(button);
13411344
1345+
if (controlloClass === "Modules\\\\Aggiornamenti\\\\Controlli\\\\IntegritaFile") {
1346+
eseguiRisoluzioneGlobaleIntegritaFile(button, restore, controlloId, controlloClass, successCallback, errorCallback, params);
1347+
1348+
return;
1349+
}
1350+
13421351
$.ajax({
13431352
url: globals.rootdir + "/actions.php",
13441353
type: "POST",
@@ -1399,6 +1408,100 @@ function eseguiRisoluzioneGlobale(button, controlloId, controlloClass, successCa
13991408
}
14001409
});
14011410
}
1411+
1412+
function eseguiRisoluzioneGlobaleIntegritaFile(button, restore, controlloId, controlloClass, successCallback, errorCallback, params = {}) {
1413+
let cardElement = $("#controllo-" + controlloId);
1414+
let bodyElement = cardElement.find(".card-body");
1415+
1416+
$.ajax({
1417+
url: globals.rootdir + "/actions.php",
1418+
type: "POST",
1419+
dataType: "JSON",
1420+
data: {
1421+
id_module: globals.id_module,
1422+
op: "controlli-action-global",
1423+
controllo: controlloClass,
1424+
params: params,
1425+
},
1426+
success: function(results) {
1427+
let summary = results.summary || "'.tr('Elaborazione in corso').'.";
1428+
bodyElement.css(\'padding\', \'10px 12px\').html(`
1429+
<div class="alert alert-info mb-0">
1430+
<i class="fa fa-spinner fa-spin mr-2"></i>${summary}
1431+
</div>
1432+
`);
1433+
1434+
if (results.more) {
1435+
if ((results.resolved_in_batch || 0) === 0) {
1436+
let stopHtml = `
1437+
<div class="alert alert-warning mb-0">
1438+
<h4><i class="fa fa-exclamation-triangle text-warning"></i> '.tr('Operazione interrotta').'</h4>
1439+
<p>'.tr('Il batch corrente non ha ridotto i record pendenti. Verificare permessi file o vincoli sui record prima di riprovare.').'</p>
1440+
</div>
1441+
`;
1442+
1443+
bodyElement.html(stopHtml);
1444+
buttonRestore(button, restore);
1445+
1446+
if (typeof errorCallback === "function") {
1447+
errorCallback();
1448+
}
1449+
1450+
return;
1451+
}
1452+
1453+
eseguiRisoluzioneGlobaleIntegritaFile(button, restore, controlloId, controlloClass, successCallback, errorCallback, params);
1454+
1455+
return;
1456+
}
1457+
1458+
let controllo = {
1459+
id: controlloId,
1460+
class: controlloClass,
1461+
name: cardElement.data("controllo-name"),
1462+
};
1463+
1464+
avviaControllo(controllo).done(function() {
1465+
if (typeof successCallback === "function") {
1466+
successCallback();
1467+
}
1468+
}).fail(function(xhr, r, error) {
1469+
let errorMessage = xhr.responseJSON && xhr.responseJSON.error ? xhr.responseJSON.error.message : error;
1470+
let refreshErrorHtml = `
1471+
<div class="alert alert-danger">
1472+
<h4><i class="fa fa-exclamation-triangle text-danger"></i> '.tr('Errore durante l\'aggiornamento del controllo').'</h4>
1473+
<p>'.tr('Si è verificato un errore').': ${errorMessage}</p>
1474+
</div>
1475+
`;
1476+
1477+
bodyElement.prepend(refreshErrorHtml);
1478+
1479+
if (typeof errorCallback === "function") {
1480+
errorCallback();
1481+
}
1482+
}).always(function() {
1483+
buttonRestore(button, restore);
1484+
});
1485+
},
1486+
error: function(xhr, r, error) {
1487+
let errorMessage = xhr.responseJSON && xhr.responseJSON.error ? xhr.responseJSON.error.message : error;
1488+
1489+
let errorHtml = `
1490+
<div class="alert alert-danger">
1491+
<h4><i class="fa fa-exclamation-triangle text-danger"></i> '.tr('Errore durante la risoluzione').'</h4>
1492+
<p>'.tr('Si è verificato un errore').': ${errorMessage}</p>
1493+
</div>
1494+
`;
1495+
1496+
bodyElement.prepend(errorHtml);
1497+
buttonRestore(button, restore);
1498+
1499+
if (typeof errorCallback === "function") {
1500+
errorCallback();
1501+
}
1502+
}
1503+
});
1504+
}
14021505
</script>
14031506
14041507
</div>';

modules/aggiornamenti/src/Controlli/IntegritaFile.php

Lines changed: 116 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
class IntegritaFile extends Controllo
2727
{
28+
protected const GLOBAL_BATCH_SIZE = 100;
29+
2830
public function getName()
2931
{
3032
return tr('Integrità file allegati');
@@ -106,36 +108,9 @@ public function execute($record, $params = [])
106108
$action = $params['action'] ?? '';
107109

108110
if ($action === 'remove_orphan_file' && $record['tipo'] === 'file_orfano') {
109-
$file_path = base_dir().'/files/'.$record['percorso_completo'];
110-
111-
if (file_exists($file_path)) {
112-
if (unlink($file_path)) {
113-
return tr('File _FILE_ rimosso con successo', ['_FILE_' => $record['nome_file']]);
114-
}
115-
116-
return tr('Errore nella rimozione del file _FILE_', ['_FILE_' => $record['nome_file']]);
117-
}
118-
119-
return tr('File _FILE_ non trovato', ['_FILE_' => $record['nome_file']]);
111+
return $this->removeOrphanFile($record)['message'];
120112
} elseif ($action === 'remove_orphan_record' && $record['tipo'] === 'file_mancante') {
121-
// Estraggo l'ID del record dal campo id (formato: missing_file_123)
122-
$upload_id = str_replace('missing_file_', '', $record['id']);
123-
124-
try {
125-
$upload = Upload::find($upload_id);
126-
if ($upload) {
127-
$upload->delete();
128-
129-
return tr('Record _FILE_ rimosso dal database con successo', ['_FILE_' => $record['nome_file']]);
130-
}
131-
132-
return tr('Record _FILE_ non trovato nel database', ['_FILE_' => $record['nome_file']]);
133-
} catch (\Exception $e) {
134-
return tr('Errore nella rimozione del record _FILE_: _ERROR_', [
135-
'_FILE_' => $record['nome_file'],
136-
'_ERROR_' => $e->getMessage(),
137-
]);
138-
}
113+
return $this->removeOrphanRecord($record)['message'];
139114
}
140115

141116
return tr('Azione non supportata per questo tipo di record');
@@ -182,87 +157,135 @@ public function solveGlobal($params = [])
182157
}
183158
}
184159
} elseif ($action === 'remove_all_both') {
185-
// Esegui entrambe le operazioni a blocchi di 100 per evitare timeout
160+
$limit = !empty($params['limit']) ? (int) $params['limit'] : self::GLOBAL_BATCH_SIZE;
161+
$limit = $limit > 0 ? $limit : self::GLOBAL_BATCH_SIZE;
186162

187-
// Separa i record per tipo
188-
$orphan_files = [];
189-
$orphan_records = [];
163+
$pending_records = $this->getPendingGlobalRecords();
164+
$total_before = count($pending_records);
165+
$batch = array_slice($pending_records, 0, $limit);
190166

191-
foreach ($this->results as $record) {
167+
$processed_files = 0;
168+
$processed_records = 0;
169+
170+
foreach ($batch as $record) {
192171
if ($record['tipo'] === 'file_orfano') {
193-
$orphan_files[] = $record;
172+
$result = $this->removeOrphanFile($record);
173+
$results[$record['id']] = $result['message'];
174+
if ($result['success']) {
175+
++$processed_files;
176+
}
194177
} elseif ($record['tipo'] === 'file_mancante') {
195-
$orphan_records[] = $record;
178+
$result = $this->removeOrphanRecord($record);
179+
$results[$record['id']] = $result['message'];
180+
if ($result['success']) {
181+
++$processed_records;
182+
}
196183
}
197184
}
198185

199-
$batch_size = 100;
200-
$processed_files = 0;
201-
$processed_records = 0;
186+
$this->results = [];
187+
$this->check();
188+
$remaining = count($this->getPendingGlobalRecords());
189+
$resolved_in_batch = max(0, $total_before - $remaining);
190+
191+
return [
192+
'batch_size' => $limit,
193+
'processed' => count($batch),
194+
'processed_files' => $processed_files,
195+
'processed_records' => $processed_records,
196+
'resolved_in_batch' => $resolved_in_batch,
197+
'remaining' => $remaining,
198+
'more' => $remaining > 0,
199+
'results' => $results,
200+
'summary' => tr('Blocco completato: _FILES_ file e _RECORDS_ record rimossi con successo. Record ancora da verificare: _REMAINING_.', [
201+
'_FILES_' => $processed_files,
202+
'_RECORDS_' => $processed_records,
203+
'_REMAINING_' => $remaining,
204+
]),
205+
'total_before' => $total_before,
206+
];
207+
}
202208

203-
// 1. Rimuovi file orfani a blocchi
204-
$file_batches = array_chunk($orphan_files, $batch_size);
205-
foreach ($file_batches as $batch) {
206-
foreach ($batch as $record) {
207-
$file_path = base_dir().'/files/'.$record['percorso_completo'];
209+
return $results;
210+
}
208211

209-
if (file_exists($file_path)) {
210-
if (unlink($file_path)) {
211-
++$processed_files;
212-
$results[$record['id']] = tr('File _FILE_ rimosso con successo', ['_FILE_' => $record['nome_file']]);
213-
} else {
214-
$results[$record['id']] = tr('Errore nella rimozione del file _FILE_', ['_FILE_' => $record['nome_file']]);
215-
}
216-
} else {
217-
$results[$record['id']] = tr('File _FILE_ non trovato', ['_FILE_' => $record['nome_file']]);
218-
}
219-
}
212+
/**
213+
* Restituisce i record ancora correggibili globalmente, dando priorità ai file orfani.
214+
*/
215+
protected function getPendingGlobalRecords()
216+
{
217+
$orphan_files = [];
218+
$orphan_records = [];
220219

221-
// Pausa breve tra i blocchi per evitare sovraccarico
222-
if (count($file_batches) > 1) {
223-
usleep(100000); // 0.1 secondi
224-
}
220+
foreach ($this->results as $record) {
221+
if ($record['tipo'] === 'file_orfano') {
222+
$orphan_files[] = $record;
223+
} elseif ($record['tipo'] === 'file_mancante') {
224+
$orphan_records[] = $record;
225225
}
226+
}
226227

227-
// 2. Rimuovi record orfani a blocchi
228-
$record_batches = array_chunk($orphan_records, $batch_size);
229-
foreach ($record_batches as $batch) {
230-
foreach ($batch as $record) {
231-
$upload_id = str_replace('missing_file_', '', $record['id']);
232-
233-
try {
234-
$upload = Upload::find($upload_id);
235-
if ($upload) {
236-
$upload->delete();
237-
++$processed_records;
238-
$results[$record['id']] = tr('Record _FILE_ rimosso dal database con successo', ['_FILE_' => $record['nome_file']]);
239-
} else {
240-
$results[$record['id']] = tr('Record _FILE_ non trovato nel database', ['_FILE_' => $record['nome_file']]);
241-
}
242-
} catch (\Exception $e) {
243-
$results[$record['id']] = tr('Errore nella rimozione del record _FILE_: _ERROR_', [
244-
'_FILE_' => $record['nome_file'],
245-
'_ERROR_' => $e->getMessage(),
246-
]);
247-
}
248-
}
228+
return array_merge($orphan_files, $orphan_records);
229+
}
249230

250-
// Pausa breve tra i blocchi per evitare sovraccarico del database
251-
if (count($record_batches) > 1) {
252-
usleep(100000); // 0.1 secondi
253-
}
231+
/**
232+
* Rimuove un file orfano dal filesystem.
233+
*/
234+
protected function removeOrphanFile($record)
235+
{
236+
$file_path = base_dir().'/files/'.$record['percorso_completo'];
237+
238+
if (file_exists($file_path)) {
239+
if (unlink($file_path)) {
240+
return [
241+
'success' => true,
242+
'message' => tr('File _FILE_ rimosso con successo', ['_FILE_' => $record['nome_file']]),
243+
];
254244
}
255245

256-
// Aggiungi un messaggio di riepilogo
257-
if ($processed_files > 0 || $processed_records > 0) {
258-
$results['summary'] = tr('Operazione completata: _FILES_ file e _RECORDS_ record rimossi con successo', [
259-
'_FILES_' => $processed_files,
260-
'_RECORDS_' => $processed_records,
261-
]);
262-
}
246+
return [
247+
'success' => false,
248+
'message' => tr('Errore nella rimozione del file _FILE_', ['_FILE_' => $record['nome_file']]),
249+
];
263250
}
264251

265-
return $results;
252+
return [
253+
'success' => false,
254+
'message' => tr('File _FILE_ non trovato', ['_FILE_' => $record['nome_file']]),
255+
];
256+
}
257+
258+
/**
259+
* Rimuove un record orfano dal database.
260+
*/
261+
protected function removeOrphanRecord($record)
262+
{
263+
$upload_id = str_replace('missing_file_', '', $record['id']);
264+
265+
try {
266+
$upload = Upload::find($upload_id);
267+
if ($upload) {
268+
$upload->delete();
269+
270+
return [
271+
'success' => true,
272+
'message' => tr('Record _FILE_ rimosso dal database con successo', ['_FILE_' => $record['nome_file']]),
273+
];
274+
}
275+
276+
return [
277+
'success' => false,
278+
'message' => tr('Record _FILE_ non trovato nel database', ['_FILE_' => $record['nome_file']]),
279+
];
280+
} catch (\Exception $e) {
281+
return [
282+
'success' => false,
283+
'message' => tr('Errore nella rimozione del record _FILE_: _ERROR_', [
284+
'_FILE_' => $record['nome_file'],
285+
'_ERROR_' => $e->getMessage(),
286+
]),
287+
];
288+
}
266289
}
267290

268291
/**

0 commit comments

Comments
 (0)