Local evaluation: unhandled promise rejection ends the process when the stream drops while the flags API is failing
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 78/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- node.js, typescript
- Ambito
- backend
Direzione di ricerca
Inizia da dist/src/local/streamer.js, in FlagConfigStreamer.start(), e segui il gestore onError che chiama this.poller.start(onChange) senza gestirne la promise. Controlla come l’altro punto di chiamata gestisce gli errori di polling e ispeziona i test pertinenti dello streamer o del poller. Il lavoro è terminato quando un errore del polling di fallback non causa più un rifiuto non gestito, mentre gli errori attesi continuano a raggiungere il chiamante.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Expected Behavior
With streamUpdates: true, if the flag stream disconnects while /sdk/v2/flags is also returning errors, the fallback poll's failure is caught and logged. The client keeps serving the configs it has cached, and the stream retry interval recovers it once Amplitude is reachable again.
Current Behavior
The SDK raises an unhandled promise rejection (Error: Flags error response: status=503). On Node 15+ that ends the process by default (exit code 1). So an incident affecting both the stream and the flags API takes down every service using local evaluation, instead of leaving them serving their last configs.
Possible Solution
In dist/src/local/streamer.js, FlagConfigStreamer.start() sets an onError handler that falls back to polling without handling the result:
this.stream.onError = (e) => {
// …
this.poller.start(onChange); // not awaited, not caught
this.startRetryStreamInterval();
};
FlagConfigPoller.start() rethrows when its first poll fails (after calling stop()), so this promise rejects with nothing to handle it. The other call site, in the catch of start(), awaits it, so that one is fine. Catching it would fix this:
this.poller.start(onChange).catch((e) => this.logger.debug('[Experiment] streamer - fallback poll failed', e));
Steps to Reproduce
- Save the script below as
repro.cjs, next to an install of@amplitude/experiment-node-server. It runs a local server that stands in for both the flags API and the stream. - Run
node repro.cjs. - The client starts over the stream and prints
started, reading on. - The script then makes both endpoints return 503 and drops the stream. The process exits with
Error: Flags error response: status=503(unhandled rejection, exit code 1), andstill runningis never printed.
const http = require('node:http');
const { Experiment } = require('@amplitude/experiment-node-server');
const configs = JSON.stringify([{
key: 'f',
metadata: { evaluationMode: 'local', flagType: 'release', deployed: true },
variants: { on: { key: 'on', value: 'on' } },
segments: [{ variant: 'on' }],
}]);
let down = false;
const streams = new Set();
const server = http.createServer((req, res) => {
if (down) return res.writeHead(503).end();
if (req.url.startsWith('/sdk/v2/flags')) return res.writeHead(200).end(configs);
if (req.url.startsWith('/sdk/stream/v1/flags')) {
res.writeHead(200, { 'content-type': 'text/event-stream' });
res.write(`data: ${configs}\n\n`);
streams.add(res);
return;
}
res.writeHead(404).end();
});
server.listen(0, '127.0.0.1', async () => {
const url = `http://127.0.0.1:${server.address().port}`;
const client = Experiment.initializeLocal('key', { serverUrl: url, streamServerUrl: url, streamUpdates: true });
await client.start();
console.log('started, reading', client.evaluateV2({ user_id: 'u' }, ['f']).f?.key);
down = true; // flags API and stream both fail from now on
streams.forEach((stream) => stream.destroy()); // drop the stream
setTimeout(() => {
console.log('still running'); // never printed
client.stop();
server.close();
}, 2000);
});
As a workaround, we mark each poller.start() promise as handled (promise.catch(() => {}) on the returned promise), so callers that await it still see the rejection.
Environment
- JS Server SDK Version: 1.13.6
- Installation Method: npm / pnpm
- Browser and Version: n/a (server-side, Node.js 22 and 24)
- Lingua principale
- TypeScript
- Stelle
- 2
- Fork
- 9
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di amplitude/experiment-node-server
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
-
user_properties from ExperimentUser are not forwarded to exposure/assignment eventsForse già presa @FabricioFFC l’ha presa 261 giorni fa. Apertabug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 68/100
amplitude/experiment-node-server#72 · 3 commenti ·
-
bug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 38/100
amplitude/experiment-node-server#69 · 1 commento ·
-
enhancement
Difficoltà 3/5 1-2 giorni Idoneità per principianti 25/100
amplitude/experiment-node-server#38 · 2 commenti · 3 reazioni ·
-
ssr-demo needs react 18Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 45/100
Tutte le issue di amplitude/experiment-node-server
Issue simili
-
[good first issue, hacktoberfest] 🏯 Hacktoberfest Task - Add new Japan Fact 898 (good-first-issue)Apertacommunity first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 86/100
lingdojo/kana-dojo#33145 · 1 commento · 5 reazioni ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
code-yeongyu/senpi#4278 ·
I maintainer di solito rispondono entro 1 giorno
-
[Bug] Activity | The activity list ignores taskIds and returns the activities of every taskForse già presa @NdekoCode l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
ever-co/ever-gauzy#11469 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
griffinwork40/agent-afk#4408 ·
I maintainer di solito rispondono entro 1 giorno