gallery_server_client_s190js/KNN-SCAN.md

9.1 KiB

Gestione dei prototipi facciali: flusso di assegnazione e aggiornamento

Progetto: server fotografico s190js Percorso di riferimento: /home/nvme/dockerdata/prove/s190js Scopo: descrivere il comportamento verificato nel codice, dalla nomina di un cluster al salvataggio dei prototipi usati per il riconoscimento facciale.

Nota sul livello di verifica: questo documento descrive il codice esaminato durante l'analisi. Non dimostra che uno specifico job sia stato completato nel database in produzione: per quello occorre controllare i record reali.

1. Panoramica

Quando si assegna una persona a un cluster dalla revisione dei volti, la route aggiorna le associazioni dei volti e richiede un aggiornamento dei prototipi per quella persona.

La richiesta viene registrata nella tabella face_prototype_jobs. Un daemon controlla la coda e avvia tools/runPrototypeWorker.js in modalità persistente (--persist). Il worker esegue la selezione dei prototipi e, se l'elaborazione riesce, salva i risultati in una transazione SQLite.

Il confronto KNN/OpenCL in modalità shadow viene eseguito dopo il salvataggio. Serve a confrontare i risultati e non decide quali prototipi vengono salvati.

2. Componenti coinvolti

Componente Responsabilità
routes/faceReview.js Gestisce l'assegnazione della persona al cluster e richiede l'aggiornamento dei prototipi.
queuePrototypeRefresh() in routes/faceReview.js Crea o aggiorna un job pending per la persona e l'utente.
Tabella face_prototype_jobs Registra le richieste, lo stato, i tentativi, gli errori e il risultato.
tools/runPrototypeDaemon.js Controlla periodicamente la coda, recupera alcuni job bloccati e avvia il worker.
tools/runPrototypeWorker.js Calcola la selezione, salva i prototipi e le valutazioni e aggiorna lo stato del job.
person_face_prototypes Contiene i prototipi facciali associati alle persone.
person_face_recognition_evaluations Contiene le valutazioni di riconoscimento calcolate dal worker.
face_recognition_index_state Indica che l'indice di riconoscimento deve essere aggiornato (dirty).
tools/faceKnnRuntime.js e tools/runFaceKnnShadow.js Eseguono il confronto KNN in modalità shadow, se abilitato e configurato.

3. Dall'assegnazione del cluster alla coda

Il frontend invia una richiesta alla route:

POST /face-review/clusters/:clusterId/assign-person

con i dati della persona e gli ID dei volti inclusi. Nel codice esaminato, la route:

  1. verifica che la persona e il cluster siano validi;
  2. verifica che i volti indicati appartengano al cluster;
  3. aggiorna le membership, segnando i volti inclusi come accettati e quelli esclusi come esclusi;
  4. assegna person_id ai volti inclusi e rimuove eventuali esclusioni corrispondenti;
  5. gestisce i volti esclusi e le relative righe in person_face_exclusions;
  6. aggiorna face_clusters.person_id;
  7. chiama queuePrototypeRefresh() con reason: 'cluster_assigned'.

Questi aggiornamenti e l'accodamento sono eseguiti nel contesto della transazione della route.

Come viene accorpata la coda

queuePrototypeRefresh() cerca un job con lo stesso user, lo stesso person_id e status = 'pending'.

  • Se lo trova: aggiorna il job esistente, compresi reason, requested_by e requested_at.
  • Se non lo trova: crea un nuovo job con status = 'pending'.

Il riutilizzo riguarda solo i job pending. Se esiste già un job processing, questa funzione non lo aggiorna: può creare un nuovo job pending.

4. Come il daemon esegue i job

Il package.json definisce:

"prototype:daemon": "node tools/runPrototypeDaemon.js",
"prototype:worker": "node tools/runPrototypeWorker.js --persist",
"prototype:once": "node tools/runPrototypeDaemon.js --once"

Il daemon:

  1. conta i job con status = 'pending';
  2. se non ne trova, attende l'intervallo configurato e ricontrolla (salvo la modalità --once);
  3. se ne trova, avvia tools/runPrototypeWorker.js --persist;
  4. dopo un successo, può avviare immediatamente un altro job pending;
  5. se il worker fallisce, registra l'errore nei log e attende prima di ricontrollare la coda.

Recupero dei job bloccati

recoverStaleJobs() può riportare da processing a pending i job che hanno started_at più vecchio della soglia staleMinutes configurata. Il job viene contrassegnato con il motivo stale_processing_recovered.

Questa è una protezione per i job rimasti bloccati in elaborazione; non equivale a un nuovo tentativo automatico di tutti i job in stato error.

5. Cosa fa il worker

5.1 Selezione e acquisizione

loadPendingJob() cerca un job pending unito alla tabella persons, ordinando per requested_at crescente. Quando inizia il salvataggio persistente, claimJob() tenta di cambiare lo stato da pending a processing, incrementando attempts e registrando la firma degli input.

Se un altro worker ha già acquisito il job, l'aggiornamento condizionale non riesce e il worker segnala un errore.

5.2 Calcolo e controlli

Il worker carica i prototipi correnti, le esclusioni e una firma dei dati di input. Esegue l'esperimento di selezione e filtra i volti esclusi. Se non resta alcun prototipo selezionato, il job fallisce.

Prima di salvare, persistResult() ricalcola la firma degli input. Se è cambiata durante l'elaborazione:

  • il job corrente diventa superseded;
  • viene creato o riutilizzato un job sostitutivo pending;
  • il risultato registra l'ID del job sostitutivo.

Questo evita di applicare una selezione calcolata su dati che nel frattempo sono cambiati.

5.3 Salvataggio dei prototipi

Quando gli input sono ancora validi, persistResult():

  1. carica le esclusioni di riferimento;
  2. mantiene attivi i prototipi bloccati (locked) che non sono esclusi;
  3. disattiva i prototipi non bloccati precedenti della persona;
  4. inserisce o aggiorna i prototipi automatici selezionati in person_face_prototypes;
  5. aggiorna l'ordine di selezione e i metadati;
  6. ricalcola e salva le valutazioni in person_face_recognition_evaluations;
  7. aggiorna face_recognition_index_state impostando status = 'dirty' e dirty = 1;
  8. porta il job a done, registra completed_at e salva il riepilogo in result_json.

Le operazioni descritte sono eseguite dentro una singola transazione SQLite. Se la transazione fallisce, le modifiche non vengono confermate.

6. Errori e stato dei job

Stato Significato nel flusso esaminato
pending Richiesta in attesa di essere elaborata.
processing Worker ha acquisito il job e lo sta elaborando.
done Salvataggio completato e risultato registrato.
error Il worker ha registrato un errore in last_error.
superseded Gli input sono cambiati durante il calcolo; è stata richiesta una nuova elaborazione.

Se il worker fallisce, markJobError() imposta status = 'error', registra completed_at e scrive il dettaglio dell'errore in last_error. Il daemon seleziona soltanto job pending: perciò un job error non viene ripreso automaticamente da quel normale ciclo di coda. Il recupero dei job processing scaduti è un meccanismo distinto.

7. KNN/OpenCL in modalità shadow

Dopo che persistResult() ha completato la transazione, il worker prova a eseguire runFaceKnnShadow().

  • Il confronto avviene dopo il salvataggio dei prototipi.
  • Non modifica i prototipi, le valutazioni o lo stato del job.
  • Un risultato incoerente viene riportato nei log.
  • Un errore dello shadow viene intercettato e registrato nei log: non annulla il salvataggio già completato.

Pertanto, la modalità shadow è un controllo comparativo e non è il componente che decide la selezione persistita.

8. Come verificare un caso reale

Per concludere che l'assegnazione di un determinato cluster abbia prodotto prototipi, bisogna verificare nel database:

  1. il job della persona e dell'utente, con motivo cluster_assigned o un motivo successivo se il job è stato accorpato;
  2. lo stato finale e, in caso di errore, il contenuto di last_error;
  3. result_json, in particolare selected_count e selected_face_ids;
  4. le righe attive di person_face_prototypes per quella persona;
  5. le valutazioni in person_face_recognition_evaluations;
  6. lo stato dell'indice in face_recognition_index_state.

Importante: l'esistenza del codice di accodamento e del worker prova che il flusso è implementato, ma soltanto i dati reali del database confermano l'esito di una specifica assegnazione.

9. File e punti del codice esaminati

  • routes/faceReview.js: route di assegnazione e queuePrototypeRefresh().
  • tools/runPrototypeDaemon.js: coda, ciclo del daemon e recupero dei job processing scaduti.
  • tools/runPrototypeWorker.js: acquisizione del job, selezione, salvataggio transazionale, gestione degli errori e chiamata allo shadow KNN.
  • package.json: script prototype:daemon, prototype:worker e prototype:once.
  • tools/faceKnnRuntime.js e tools/runFaceKnnShadow.js: runtime e confronto KNN shadow.

Documento descrittivo basato sul codice ispezionato; verificare il codice e il database della versione in esecuzione prima di usarlo come specifica operativa definitiva.