Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Cerrado Apto para principiantes
#9,356 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Ya se ha fusionado un pull request relacionado.

  • #9389 de @effect-bot — fusionado

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
75/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
typescript

Línea de trabajo

El paso de adquisición de registerProducer en packages/opentelemetry/src/OtelMetrics.ts (alrededor de las líneas 82-99) vincula cada reader dentro de un único Effect.sync, así que si se lanza una excepción a mitad de camino, los readers vinculados antes quedan sin finalizer. Lee el bloque de adquisición y el finalizer de acquireRelease, y luego ejecuta los dos casos de registerProducer en packages/opentelemetry/test/OtelMetrics.test.ts. Está terminado cuando pasa el test que espera que los readers ya vinculados se apaguen tras un bind fallido, y sigue pasando el test existente de apagado al cerrar el scope.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
TypeScript
Estrellas
16.7k
Forks
808
Merge medio
11 h 33 min
PR fusionados (30 d)
855

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de Effect-TS/effect

Todos los issues de Effect-TS/effect

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.