Contact-Sync: schnellerer Retry (3min statt 30min) nach fehlgeschlagenem Sync, z.B. bei abgelaufener Session

This commit is contained in:
dimedtec 2026-08-31 11:32:35 +02:00
parent 3500d75b42
commit e4cbec50f4

View file

@ -10,6 +10,13 @@ namespace Anrufmonitor.Contacts;
/// Der eigentliche Anruf-Lookup (<see cref="ContactLookupService"/>) liest danach
/// nur noch aus dem Cache und ruft nie live gegen Office365/Google waehrend eines
/// eingehenden Anrufs.
///
/// Adaptives Intervall: nach einem erfolgreichen Durchlauf wird das normale, lange
/// <see cref="ContactsOptions.SyncInterval"/> gewartet. Ist mindestens ein Provider
/// fehlgeschlagen (z. B. weil ein Tenant eine kurze Session-Lifetime erzwingt und ein
/// neuer interaktiver Login noetig ist, der Device-Code aber noch nicht eingegeben wurde),
/// wird stattdessen zeitnah erneut versucht - sonst muesste man bis zu 30 Minuten auf den
/// naechsten (rechtzeitig ablaufenden) Login-Code warten.
/// </summary>
public sealed class ContactSyncService(
ContactCache cache,
@ -18,6 +25,8 @@ public sealed class ContactSyncService(
ILogger<ContactSyncService> logger)
: BackgroundService
{
private static readonly TimeSpan RetryIntervalAfterFailure = TimeSpan.FromMinutes(3);
private readonly ContactsOptions _options = options.Value;
public event EventHandler? SyncCompleted;
@ -26,19 +35,28 @@ public sealed class ContactSyncService(
{
await cache.EnsureCreatedAsync(stoppingToken);
using var timer = new PeriodicTimer(_options.SyncInterval);
// Erster Sync sofort beim Start, danach im konfigurierten Intervall.
do
while (!stoppingToken.IsCancellationRequested)
{
await SyncOnceAsync(stoppingToken);
var allSucceeded = await SyncOnceAsync(stoppingToken);
var delay = allSucceeded ? _options.SyncInterval : RetryIntervalAfterFailure;
try
{
await Task.Delay(delay, stoppingToken);
}
catch (OperationCanceledException)
{
break;
}
}
while (!stoppingToken.IsCancellationRequested
&& await timer.WaitForNextTickAsync(stoppingToken));
}
public async Task SyncOnceAsync(CancellationToken cancellationToken)
/// <summary>Gibt zurueck, ob ALLE aktivierten Provider erfolgreich synchronisiert wurden.</summary>
public async Task<bool> SyncOnceAsync(CancellationToken cancellationToken)
{
var allSucceeded = true;
foreach (var provider in providers.Where(p => p.IsEnabled))
{
try
@ -48,10 +66,12 @@ public sealed class ContactSyncService(
}
catch (Exception ex)
{
allSucceeded = false;
logger.LogError(ex, "Sync fuer {Source} fehlgeschlagen", provider.Source);
}
}
SyncCompleted?.Invoke(this, EventArgs.Empty);
return allSucceeded;
}
}