Une clef de session ecrite deux fois n est pas deux sessions

Airtria envoie la clef telle que PostgreSQL l affiche — 2026-09-12
11:55:54.493+02 — et la base me la rend en ISO. Meme instant, caracteres
differents : mon Set les comptait deux fois, et le journal annoncait « 2
sessions, dont 1 ajoutee par le branchement » la ou le branchement n en
contient qu une.

L UPDATE resistait, les deux formes se transtypant en timestamptz. Ce qui ne
resistait pas, c est le tableau sessions du detail, qui portait le doublon —
et c est precisement lui que le controle de contradiction interroge sur les
etiquettes suivantes.

Trouve par le journal du service en essai a blanc, trois minutes avant la
bascule du kill-switch. Mon harnais de verification ne pouvait pas le voir :
il comptait les lignes SQL, pas les elements du Set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
This commit is contained in:
Tallagrand Thierry
2026-09-14 02:21:05 +02:00
parent d8ef69b584
commit 9ccbcc0619

View File

@@ -1032,7 +1032,18 @@ async function appliquerEtiquetteConcierge(sql, client, st) {
// evenement « on » a 15:55:15.485, soit 62 ms plus tard. Sans cette marge,
// la session qui OUVRE un branchement est exclue de son propre groupe — ce
// que le premier essai a blanc a effectivement montre (2 sessions sur 3).
const cibles = new Set(demandees);
// ⚠️ NORMALISER AVANT DE DEDUPLIQUER. Airtria envoie sa clef telle que
// PostgreSQL l'affiche — « 2026-09-12 11:55:54.493+02 » — et la base me la
// rend en ISO — « 2026-09-12T09:55:54.493Z ». Meme instant, caracteres
// differents : un Set les compte DEUX FOIS. L'UPDATE resistait (les deux se
// transtypent en timestamptz) mais le journal annoncait « 2 sessions, dont 1
// ajoutee par le branchement » pour UNE seule session — et surtout le
// tableau `sessions` du detail portait le doublon, alors que c'est lui que
// le controle de contradiction interroge sur les etiquettes suivantes.
// Une clef illisible est gardee telle quelle : mieux vaut une clef brute
// qu'une clef inventee.
const enIso = (x) => { const d = new Date(x); return Number.isNaN(+d) ? String(x) : d.toISOString(); };
const cibles = new Set(demandees.map(enIso));
let frontiereConnue = true;
for (const d of demandees) {
const { rows } = await lire(
@@ -1053,7 +1064,7 @@ async function appliquerEtiquetteConcierge(sql, client, st) {
// Aucune borne : le journal ne remonte pas assez loin. On n'invente pas
// une frontiere qu'on n'a pas observee — on s'en tient a la liste recue.
if (!rows.length) { frontiereConnue = false; continue; }
for (const r of rows) cibles.add(new Date(r.debut).toISOString());
for (const r of rows) cibles.add(enIso(r.debut));
}
// Deux etiquettes DIFFERENTES sur le meme cable : soit une frontiere fausse,