2025 © Francesco Costantino

E se migliaia di utenti vivessero nel passato... o nel futuro?

3 settembre 2025

E se migliaia di utenti vivessero nel passato... o nel futuro?

E se migliaia di utenti vivessero nel passato... o nel futuro?

Sembra fantascienza, ma è stato un problema reale che abbiamo affrontato con la nostra app HbbTV. Per un'applicazione che dipende da una temporizzazione perfetta, ci siamo trovati in un paradosso temporale.

Il Problema

Stavamo osservando bug bizzarri che non riuscivamo a replicare. La svolta è arrivata quando abbiamo iniziato a registrare l'orologio interno di ogni TV che eseguiva la nostra app. L'invio di questi dati al nostro stack ELK ha rivelato uno scenario caotico: migliaia di Smart TV in tutta Italia erano fuori sync. Senza una corretta configurazione NTP, i loro orologi stavano derivando di diversi minuti, creando un incubo per le nostre funzionalità time-sensitive.

I nostri utenti non erano viaggiatori del tempo; le loro TV avevano semplicemente l'ora sbagliata.

La Soluzione

Per risolvere questo problema, abbiamo deciso di diventare noi stessi i padroni del tempo. Ecco come abbiamo implementato la soluzione utilizzando Angular:

// time-sync.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable, map, tap } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class TimeSyncService {
  private timeOffset: number = 0;

  constructor(private http: HttpClient) {
    this.synchronizeTime();
  }

  private synchronizeTime() {
    this.http.get('/api/time', { observe: 'response' })
      .subscribe((response: HttpResponse<any>) => {
        const serverTime = new Date(response.headers.get('Date')).getTime();
        const localTime = new Date().getTime();
        this.timeOffset = serverTime - localTime;
        console.log(`Time offset: ${this.timeOffset}ms`);
      });
  }

  public getCurrentTime(): Date {
    return new Date(Date.now() + this.timeOffset);
  }
}

E come utilizzarlo nei componenti:

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { TimeSyncService } from './time-sync.service';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h2>Ora corretta: {{ currentTime | date:'medium' }}</h2>
      <h2>Ora locale del dispositivo: {{ deviceTime | date:'medium' }}</h2>
    </div>
  `
})
export class AppComponent implements OnInit {
  currentTime: Date;
  deviceTime: Date;

  constructor(private timeSync: TimeSyncService) {
    setInterval(() => {
      this.currentTime = this.timeSync.getCurrentTime();
      this.deviceTime = new Date();
    }, 1000);
  }
}

Come Funziona

Il nostro servizio TimeSyncService fa diverse cose importanti:

  1. All'avvio dell'applicazione, fa una richiesta HTTP al nostro server
  2. Estrae l'header Date dalla risposta HTTP come "verità assoluta"
  3. Calcola la differenza (offset) tra l'ora del server e quella del dispositivo
  4. Fornisce un metodo getCurrentTime() che restituisce sempre l'ora corretta

Per gestire i casi edge, abbiamo anche implementato una strategia di retry e fallback:

// retry-strategy.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { retryWhen, delay, take } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class TimeRetryStrategy {
  private readonly MAX_RETRIES = 3;
  private readonly RETRY_DELAY = 1000;
  private readonly FALLBACK_SERVERS = [
    '/api/time',
    '/api/fallback-time',
    'https://backup-timeserver.com/time'
  ];

  constructor(private http: HttpClient) {}

  public getTimeWithRetry() {
    return this.http.get(this.FALLBACK_SERVERS[0])
      .pipe(
        retryWhen(errors => 
          errors.pipe(
            delay(this.RETRY_DELAY),
            take(this.MAX_RETRIES)
          )
        )
      );
  }
}

Risultati

Il risultato? Tutti i bug relativi al tempo sono svaniti istantaneamente. La nostra app HbbTV ora funziona perfettamente su tutte le Smart TV, indipendentemente dalle loro impostazioni di ora locale.

Lezioni Apprese

Questa esperienza ha rafforzato una regola d'oro dello sviluppo: il client-side è il selvaggio west. Costruire un solido sistema di monitoraggio e observability è l'unico modo per domarlo.

Best Practices Emerse:

  1. Mai fidarsi del clock del dispositivo client
  2. Implementare sempre un sistema di logging robusto
  3. Utilizzare ELK o strumenti simili per l'analisi dei pattern
  4. Avere sempre un piano di fallback