Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

OtelMetrics.registerProducer: readers bound before one that fails to bind are never shut down and keep exporting

Aperta Adatta ai principianti
#9,356 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
75/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
typescript
Ambito
observability

Direzione di ricerca

Il passo di acquisizione di registerProducer in packages/opentelemetry/src/OtelMetrics.ts (intorno alle righe 82-99) associa ogni reader dentro un unico Effect.sync, quindi un'eccezione lanciata a metà lascia i reader già associati senza finalizer. Leggi il blocco di acquisizione e il finalizer di acquireRelease, poi esegui i due casi di registerProducer in packages/opentelemetry/test/OtelMetrics.test.ts. È completato quando passa il test che si aspetta che i reader già associati vengano chiusi dopo un bind fallito e continua a passare il test esistente sulla chiusura alla chiusura dello scope.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

What version of Effect is running?

4.0.5 (effect, @effect/opentelemetry; @opentelemetry/sdk-metrics 2.12.0, Node 26.11.1). registerProducer is unchanged on main at c9a1e72.

What steps can reproduce the bug?

When the reader thunk given to OtelMetrics.layer or registerProducer returns several readers and a later one throws from setMetricProducer, the build dies but the earlier readers stay bound and are never shut down. For example, the later reader may already be registered with a MeterProvider, so the SDK throws "MetricReader can not be bound to a MeterProvider again.". A PeriodicExportingMetricReader among the earlier readers keeps exporting Effect metrics after the scope has closed. If the build failure is handled, for example with a Layer.catchCause fallback, it keeps exporting for the life of the process with no owner.

This test, added to packages/opentelemetry/test/OtelMetrics.test.ts (with Cause and Exit imports), fails on main. The first case passes:

describe("registerProducer", () => {
  class Reader extends MetricReader {
    shutdowns = 0
    protected onShutdown(): Promise<void> {
      this.shutdowns++
      return Promise.resolve()
    }
    protected onForceFlush(): Promise<void> {
      return Promise.resolve()
    }
  }

  it.effect("shuts down every reader when the scope closes", () =>
    Effect.gen(function*() {
      const services = yield* Effect.context<never>()
      const producer = new internal.MetricProducerImpl(resourceFromAttributes({}), services)
      const first = new Reader()
      const second = new Reader()
      yield* OtelMetrics.registerProducer(producer, () => [first, second]).pipe(Effect.scoped)
      assert.deepStrictEqual([first.shutdowns, second.shutdowns], [1, 1])
    }))

  it.effect("shuts down readers bound before a reader that fails to bind", () =>
    Effect.gen(function*() {
      const services = yield* Effect.context<never>()
      const producer = new internal.MetricProducerImpl(resourceFromAttributes({}), services)
      const fresh = new Reader()
      const alreadyBound = new Reader()
      alreadyBound.setMetricProducer(producer)
      const exit = yield* OtelMetrics.registerProducer(producer, () => [fresh, alreadyBound]).pipe(
        Effect.scoped,
        Effect.exit
      )
      assert.isTrue(Exit.isFailure(exit) && Cause.hasDies(exit.cause))
      assert.strictEqual(fresh.shutdowns, 1)
    }))
})
What is the expected behavior?

Every reader that registerProducer bound is shut down when the acquisition fails, as the release does when the scope closes. Two separate registerProducer calls in one scope already do this: when the second call dies, the first call's finalizer shuts its reader down.

What do you see instead?

expected +0 to equal 1: fresh is bound but never shut down.

The published 4.0.5 packages behave the same way. In that run the readers were PeriodicExportingMetricReaders with a 50 ms interval. In the 300 ms after the failed Effect.provide(OtelMetrics.layer(() => [a, alreadyBound])), reader a exported 5 more times, and its exporter's shutdown was never called.

Additional information

The acquire binds every reader inside one Effect.sync (OtelMetrics.ts#L82-L99). When setMetricProducer throws for a later reader, the earlier ones are already bound. The throw becomes a defect, and acquireRelease adds its finalizer only after acquisition succeeds (effect.ts#L4153-L4155), so nothing shuts them down. main still has the same code. Shutting down the readers bound so far when a later bind throws would cover it. The v3 Metrics.registerProducer (0.64.1) binds readers the same way.

Lingua principale
TypeScript
Stelle
16.7k
Fork
808
Merge medio
9h 42m
PR unite (30g)
669

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Effect-TS/effect

Tutte le issue di Effect-TS/effect

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.