- Aggiunto supporto schedulazione con intervalli flessibili (secondi/minuti/ore/giorni/settimane/mesi) - Esteso modello ProfileSchedule con campi IntervalValue e IntervalUnit - Ottimizzato ScheduledJobService per controlli ogni 30s con esecuzione parallela - Implementata interfaccia UI completa con anteprima real-time in italiano - Aggiunta migrazione database AddIntervalSchedulingFields - Implementati metodi calcolo NextExecutionTime per intervalli - Aggiunta gestione tracking anti-duplicati e cleanup automatico - Creata documentazione completa (6 file, 2500+ righe) Modifiche tecniche: - ProfileSchedule.cs: Nuovi campi e metodi CalculateNextInterval/GetScheduleDescription - ScheduledJobService.cs: Ridotto check interval a 30s, aggiunto parallel processing - ProfileScheduleService.cs: Supporto calcolo intervalli in UpdateNextExecutionTimeAsync - Scheduling.razor: Aggiunta sezione UI per configurazione intervalli - Scheduling.razor.cs: Implementato GetIntervalPreview() e gestione stato campi
15 KiB
Sistema di Schedulazione Avanzata - Data Coupler
📋 Panoramica
Data: 2 Ottobre 2025
Feature: Sistema di schedulazione completo con supporto per intervalli personalizzati
Versione: 2.0
🚀 Funzionalità Implementate
Tipi di Schedulazione Supportati
-
✅ Una Volta (Once)
- Esecuzione singola a una data/ora specifica
- Ideale per migrazioni one-time o test
-
✅ Giornaliera (Daily)
- Esecuzione ogni giorno a un orario specifico
- Esempio: Ogni giorno alle 02:00
-
✅ Settimanale (Weekly)
- Esecuzione ogni settimana in un giorno specifico
- Esempio: Ogni Lunedì alle 08:00
-
✅ Mensile (Monthly)
- Esecuzione ogni mese in un giorno specifico
- Esempio: Il giorno 1 di ogni mese alle 00:00
-
🆕 A Intervalli (Interval) - NUOVA!
- Esecuzione ricorrente ogni N unità di tempo
- Supporta:
- Secondi: Ogni N secondi (utile per test/demo)
- Minuti: Ogni N minuti (es. ogni 5, 10, 15, 30 minuti)
- Ore: Ogni N ore (es. ogni 1, 2, 4, 6, 12 ore)
- Giorni: Ogni N giorni (es. ogni 2, 3, 7 giorni)
- Settimane: Ogni N settimane (es. ogni 2, 4 settimane)
- Mesi: Ogni N mesi (es. ogni 2, 3, 6 mesi)
🔧 Modifiche Implementate
1. Modello Dati - ProfileSchedule
File: CredentialManager/Models/ProfileSchedule.cs
Nuovi Campi
// Configurazione per schedulazioni a intervalli
public int? IntervalValue { get; set; } // Valore dell'intervallo (es. 5, 10, 30)
public string? IntervalUnit { get; set; } // "seconds", "minutes", "hours", "days", "weeks", "months"
Nuovi Metodi
/// <summary>
/// Calcola la prossima esecuzione basandosi sulla data/ora base fornita
/// Per intervalli, aggiunge l'intervallo alla data base
/// </summary>
public DateTime? CalculateNextExecutionFromLast()
/// <summary>
/// Calcola la prossima esecuzione aggiungendo l'intervallo alla data base
/// </summary>
private DateTime CalculateNextInterval(DateTime baseTime)
/// <summary>
/// Ottiene una descrizione leggibile della schedulazione
/// </summary>
public string GetScheduleDescription()
Esempi di Utilizzo
// Ogni 5 minuti
var schedule = new ProfileSchedule
{
ScheduleType = "interval",
IntervalValue = 5,
IntervalUnit = "minutes"
};
// Descrizione: "Ogni 5 minuti"
// Ogni 2 ore
var schedule = new ProfileSchedule
{
ScheduleType = "interval",
IntervalValue = 2,
IntervalUnit = "hours"
};
// Descrizione: "Ogni 2 ore"
// Ogni 30 secondi (utile per test)
var schedule = new ProfileSchedule
{
ScheduleType = "interval",
IntervalValue = 30,
IntervalUnit = "seconds"
};
// Descrizione: "Ogni 30 secondi"
2. Background Service - ScheduledJobService
File: Data_Coupler/BackgroundServices/ScheduledJobService.cs
Miglioramenti Principali
✅ Intervallo di Controllo Ridotto
// PRIMA: TimeSpan.FromMinutes(1) // Controllo ogni minuto
// DOPO: TimeSpan.FromSeconds(30) // Controllo ogni 30 secondi
Motivazione: Supportare schedulazioni a intervalli brevi (secondi/minuti) richiede controlli più frequenti.
✅ Esecuzione Parallela
// Esegue più schedulazioni contemporaneamente
_ = Task.Run(async () =>
{
await ExecuteScheduleAsync(schedule, cancellationToken);
}, cancellationToken);
Benefici:
- Più schedulazioni possono essere eseguite simultaneamente
- Non blocca altre schedulazioni se una è lenta
- Migliora throughput complessivo
✅ Tracking Esecuzioni
private readonly Dictionary<int, DateTime> _runningSchedules = new();
private bool IsScheduleRunning(int scheduleId)
private void MarkScheduleAsRunning(int scheduleId)
private void MarkScheduleAsCompleted(int scheduleId)
private void CleanupRunningSchedules() // Rimuove schedulazioni bloccate dopo 1 ora
Protezioni:
- Previene esecuzioni duplicate della stessa schedulazione
- Rileva e pulisce schedulazioni bloccate (timeout 1 ora)
- Thread-safe con lock
✅ Tolleranza Temporale Adattiva
var tolerance = schedule.ScheduleType == "interval"
? TimeSpan.FromSeconds(30) // 30 secondi per intervalli
: TimeSpan.FromMinutes(1); // 1 minuto per altre schedulazioni
Motivazione: Schedulazioni a intervalli necessitano tolleranza più stretta per precisione.
3. Service Layer - ProfileScheduleService
File: CredentialManager/Services/ProfileScheduleService.cs
Aggiornamenti
public async Task UpdateNextExecutionTimeAsync(int scheduleId)
{
var schedule = await _context.ProfileSchedules.FindAsync(scheduleId);
// Per schedulazioni a intervallo, calcola dalla ultima esecuzione
if (schedule.ScheduleType == "interval")
{
schedule.NextExecutionTime = schedule.CalculateNextExecutionFromLast();
}
else
{
schedule.NextExecutionTime = schedule.CalculateNextExecution();
}
await _context.SaveChangesAsync();
}
Differenza Chiave:
- Intervalli: Calcola da
LastExecutionTime(tempo trascorso + intervallo) - Altri tipi: Calcola da
Now(prossimo orario programmato)
4. Database Migration
File: CredentialManager/Migrations/[timestamp]_AddIntervalSchedulingFields.cs
Modifiche Schema
ALTER TABLE ProfileSchedules
ADD IntervalValue INT NULL;
ALTER TABLE ProfileSchedules
ADD IntervalUnit NVARCHAR(20) NULL;
Compatibilità: Campi nullable, schedulazioni esistenti non impattate.
📊 Esempi Pratici di Utilizzo
Caso 1: Sincronizzazione Frequente (Ogni 5 Minuti)
{
"Name": "Sincronizzazione Clienti Frequente",
"ProfileId": 1,
"ScheduleType": "interval",
"IntervalValue": 5,
"IntervalUnit": "minutes",
"IsEnabled": true
}
Esecuzione:
10:00:00 - Prima esecuzione
10:05:00 - Seconda esecuzione (+5 minuti)
10:10:00 - Terza esecuzione (+5 minuti)
10:15:00 - Quarta esecuzione (+5 minuti)
...
Caso 2: Backup Orario
{
"Name": "Backup Orario Prodotti",
"ProfileId": 2,
"ScheduleType": "interval",
"IntervalValue": 1,
"IntervalUnit": "hours",
"IsEnabled": true
}
Esecuzione:
08:00:00 - Prima esecuzione
09:00:00 - Seconda esecuzione (+1 ora)
10:00:00 - Terza esecuzione (+1 ora)
11:00:00 - Quarta esecuzione (+1 ora)
...
Caso 3: Test Rapido (Ogni 30 Secondi)
{
"Name": "Test Sync Veloce",
"ProfileId": 3,
"ScheduleType": "interval",
"IntervalValue": 30,
"IntervalUnit": "seconds",
"IsEnabled": true
}
Esecuzione:
14:30:00 - Prima esecuzione
14:30:30 - Seconda esecuzione (+30 secondi)
14:31:00 - Terza esecuzione (+30 secondi)
14:31:30 - Quarta esecuzione (+30 secondi)
...
⚠️ Attenzione: Intervalli molto brevi (<1 minuto) dovrebbero essere usati solo per test o scenari specifici.
Caso 4: Sincronizzazione Bi-Settimanale
{
"Name": "Sync Archivio Bi-Settimanale",
"ProfileId": 4,
"ScheduleType": "interval",
"IntervalValue": 2,
"IntervalUnit": "weeks",
"IsEnabled": true
}
Esecuzione:
01/10/2025 00:00 - Prima esecuzione
15/10/2025 00:00 - Seconda esecuzione (+2 settimane)
29/10/2025 00:00 - Terza esecuzione (+2 settimane)
12/11/2025 00:00 - Quarta esecuzione (+2 settimane)
...
🎯 Logica di Schedulazione
Calcolo Prossima Esecuzione
Per Schedulazioni a Intervallo
NextExecutionTime = LastExecutionTime + Intervallo
Esempio:
- LastExecutionTime = 14:30:00
- IntervalValue = 15
- IntervalUnit = "minutes"
- NextExecutionTime = 14:30:00 + 15 minuti = 14:45:00
Comportamento Primo Avvio:
Se LastExecutionTime è NULL (mai eseguita):
NextExecutionTime = DateTime.Now + Intervallo
Per Altre Schedulazioni
NextExecutionTime = Prossimo Orario Programmato
Esempio Daily:
- DailyTime = "02:00"
- Now = 14:30
- NextExecutionTime = Domani alle 02:00
Controllo Esecuzioni
Ogni 30 secondi il ScheduledJobService:
1. Recupera tutte le schedulazioni attive
2. Filtra quelle con NextExecutionTime <= Now + Tolleranza
3. Verifica che non siano già in esecuzione
4. Le esegue in parallelo (Task.Run)
5. Aggiorna NextExecutionTime dopo completamento
Tolleranza Temporale:
- Intervalli: ±30 secondi
- Altri tipi: ±1 minuto
⚡ Performance e Ottimizzazioni
Esecuzione Parallela
// Più schedulazioni possono essere eseguite contemporaneamente
foreach (var schedule in pendingSchedules)
{
_ = Task.Run(async () =>
{
await ExecuteScheduleAsync(schedule, cancellationToken);
}, cancellationToken);
}
Vantaggi:
- ✅ Throughput migliorato
- ✅ Schedulazioni lente non bloccano altre
- ✅ Migliore utilizzo risorse
Protezioni:
- ✅ Tracking per evitare duplicati
- ✅ Timeout dopo 1 ora (pulizia automatica)
- ✅ Gestione errori isolata per schedulazione
Controlli Più Frequenti
// Controllo ogni 30 secondi invece di 1 minuto
private TimeSpan _checkInterval = TimeSpan.FromSeconds(30);
Motivazione:
- Supportare intervalli brevi (es. ogni 30 secondi, ogni minuto)
- Maggiore precisione temporale
- Impatto trascurabile su performance (query leggere)
🔍 Monitoraggio e Debug
Logging Dettagliato
// Ogni controllo (LogTrace - solo se abilitato)
_logger.LogTrace("Nessuna schedulazione in sospeso trovata");
// Esecuzioni trovate (LogInformation)
_logger.LogInformation("Trovate {Count} schedulazioni da eseguire", count);
// Schedulazioni già running (LogDebug)
_logger.LogDebug("Schedulazione {ScheduleId} già in esecuzione, salto", id);
// Aggiornamento prossima esecuzione (LogDebug)
_logger.LogDebug("Prossima esecuzione aggiornata: {NextExecution} (tipo: {Type})",
nextTime, scheduleType);
// Completamento con successo (LogInformation)
_logger.LogInformation("Schedulazione {ScheduleId} eseguita: {Records} record, {Duration}s",
id, records, duration);
// Errori (LogError)
_logger.LogError(ex, "Errore durante l'esecuzione schedulazione {ScheduleId}", id);
Query Monitoraggio
-- Schedulazioni attive per tipo
SELECT
ScheduleType,
COUNT(*) as Total,
SUM(CASE WHEN IsEnabled = 1 THEN 1 ELSE 0 END) as Enabled
FROM ProfileSchedules
WHERE IsActive = 1
GROUP BY ScheduleType;
-- Schedulazioni a intervallo con dettagli
SELECT
Id,
Name,
IntervalValue,
IntervalUnit,
CONCAT(IntervalValue, ' ', IntervalUnit) as Interval,
LastExecutionTime,
NextExecutionTime,
LastExecutionStatus
FROM ProfileSchedules
WHERE ScheduleType = 'interval'
AND IsEnabled = 1
ORDER BY NextExecutionTime;
-- Statistiche esecuzioni ultima ora
SELECT
s.Name,
s.ScheduleType,
COUNT(h.Id) as ExecutionCount,
AVG(DATEDIFF(SECOND, h.StartTime, h.EndTime)) as AvgDurationSeconds,
SUM(h.RecordsProcessed) as TotalRecords
FROM ProfileSchedules s
LEFT JOIN ScheduleExecutionHistory h ON s.Id = h.ScheduleId
WHERE h.StartTime >= DATEADD(HOUR, -1, GETDATE())
GROUP BY s.Id, s.Name, s.ScheduleType
ORDER BY ExecutionCount DESC;
-- Schedulazioni bloccate (running da >1 ora)
SELECT
Id,
Name,
LastExecutionStatus,
LastExecutionTime,
DATEDIFF(MINUTE, LastExecutionTime, GETDATE()) as MinutesSinceExecution
FROM ProfileSchedules
WHERE LastExecutionStatus = 'running'
AND LastExecutionTime < DATEADD(HOUR, -1, GETDATE());
⚠️ Limitazioni e Considerazioni
Intervalli Molto Brevi (<1 Minuto)
⚠️ Attenzione:
- Intervalli di pochi secondi generano molte esecuzioni
- Aumentano carico database e API
- Dovrebbero essere usati solo per:
- Test e demo
- Scenari critici real-time
- Ambienti con risorse dedicate
Raccomandazioni:
- Produzione: Minimo 5-10 minuti
- Test: 30 secondi - 2 minuti OK
- Dev: Qualsiasi intervallo
Esecuzioni Parallele
Comportamento:
- Più schedulazioni possono essere eseguite contemporaneamente
- Stessa schedulazione NON può essere eseguita due volte in parallelo
Implicazioni:
- Se una schedulazione impiega più tempo del suo intervallo, alcune esecuzioni verranno saltate
- Esempio: Intervallo 5 minuti, ma esecuzione richiede 7 minuti → Una esecuzione verrà saltata
Soluzione:
- Aumentare l'intervallo
- Ottimizzare il profilo di trasferimento
- Monitorare durata esecuzioni
Drift Temporale
Per Intervalli Lunghi (giorni/settimane/mesi):
Esempio: Ogni 2 settimane
- Prima esecuzione: 01/10/2025 14:30:00
- Seconda esecuzione: 15/10/2025 14:30:00 (esatta)
- Terza esecuzione: 29/10/2025 14:30:00 (esatta)
Se una esecuzione ritarda:
- Seconda esecuzione: 15/10/2025 14:35:00 (ritardo 5 minuti)
- Terza esecuzione: 29/10/2025 14:35:00 (drift propagato)
Mitigazione:
- Tolleranza di ±30 secondi assorbe piccole variazioni
- Per intervalli lunghi, il drift è trascurabile (secondi su giorni)
- Considerare schedulazioni daily/weekly/monthly se serve orario esatto
🧪 Testing
Test Schedulazione a 30 Secondi
var schedule = new ProfileSchedule
{
Name = "Test Rapid Sync",
ProfileId = 1,
ScheduleType = "interval",
IntervalValue = 30,
IntervalUnit = "seconds",
IsEnabled = true,
IsActive = true
};
await scheduleService.CreateScheduleAsync(schedule);
// Osserva i log ogni 30 secondi:
// 14:30:00 - Esecuzione 1
// 14:30:30 - Esecuzione 2
// 14:31:00 - Esecuzione 3
Test Schedulazione a 5 Minuti
var schedule = new ProfileSchedule
{
Name = "Test 5 Minutes Sync",
ProfileId = 2,
ScheduleType = "interval",
IntervalValue = 5,
IntervalUnit = "minutes",
IsEnabled = true,
IsActive = true
};
await scheduleService.CreateScheduleAsync(schedule);
// Osserva i log ogni 5 minuti:
// 10:00 - Esecuzione 1
// 10:05 - Esecuzione 2
// 10:10 - Esecuzione 3
📝 Checklist Deployment
Pre-Deploy
- Migration database creata
- Codice compilato senza errori
- Logging configurato correttamente
- Backup database production
- Test in ambiente staging
Deploy
-
Stop servizio esistente
Stop-Service -Name "DataCouplerService" -
Backup database
BACKUP DATABASE CredentialDb TO DISK = 'backup_pre_scheduling.bak' -
Esegui migration
cd CredentialManager dotnet ef database update --context CredentialDbContext -
Deploy nuova versione
dotnet publish --configuration Release -
Avvia servizio
Start-Service -Name "DataCouplerService" -
Verifica logs
Get-EventLog -LogName Application -Source "ScheduledJobService" -Newest 50
Post-Deploy
- Verificare schedulazioni esistenti funzionano
- Creare schedulazione test a intervalli
- Monitorare logs per 24 ore
- Verificare performance sistema
- Validare accuratezza temporale
🔗 File Modificati
CredentialManager/Models/ProfileSchedule.cs- Modello con nuovi campiCredentialManager/Services/ProfileScheduleService.cs- Logica aggiornataData_Coupler/BackgroundServices/ScheduledJobService.cs- Background service miglioratoCredentialManager/Migrations/[timestamp]_AddIntervalSchedulingFields.cs- Migration database
Versione: 2.0
Data Implementazione: 2 Ottobre 2025
Status: ✅ Implementato e Testato
Sviluppatore: Alessio Dalsanto