Ember.js: [2.15.0] - Ember construye errores de memoria al usar herramientas de CI

Creado en 7 sept. 2017  ·  23Comentarios  ·  Fuente: emberjs/ember.js

La transpilación paralela de Babel se introdujo en Ember 2.15. (https://github.com/babel/broccoli-babel-transpiler#number-of-jobs)

De forma predeterminada, broccoli-babel-transpiler utiliza los recursos del sistema (cpus) para determinar la cantidad de trabajos que se pueden ejecutar en paralelo.

La mayoría de las herramientas de CI modernas usan la ventana acoplable para ayudar a aislar las compilaciones de otras compilaciones que se ejecutan en un servidor determinado. Esto les permite usar máquinas virtuales grandes para ejecutar muchas compilaciones que están en gran medida aisladas entre sí. Por ejemplo, las máquinas virtuales en las que se ejecuta Circle CI suelen tener 36 núcleos de CPU... Pero las compilaciones en sí están limitadas a dos.

El problema surge cuando se intenta utilizar medios tradicionales para determinar qué recursos están disponibles para el programa. Por ejemplo, para determinar la cantidad de CPU que usan el nodo, puede hacer node -e "console.log(require('os').cpus().length);" . Sin embargo, esta información informa los recursos de la instancia, no los recursos limitados disponibles para el contenedor acoplable. El resultado es que lo que sea que se esté ejecutando piense que tiene acceso a 36 núcleos, pero en realidad solo tiene dos.

He creado un repositorio de ejemplo:
https://github.com/mwisner/ember-circleci-example en el que puede ver el historial de compilaciones aprobadas y fallidas (https://circleci.com/gh/mwisner/ember-circleci-example) (https:// travis-ci.org/mwisner/ember-circleci-example/builds) (Aún no he arreglado la compilación de travis).

Herramientas de CI probadas
-- Círculo CI 2.0
-- Círculo CI 1.0
-- Travis CI (falla con Travis CI proporcionado por defecto)

Documentos de trabajos paralelos: https://github.com/babel/broccoli-babel-transpiler#number -of-jobs

Cuando aparecen errores, se ven como el siguiente resultado. Sin embargo, en muchas situaciones (como travis CI) simplemente se agota después de 10 minutos sin ninguna información)

#!/bin/bash -eo pipefail
ember test
Could not start watchman
Visit https://ember-cli.com/user-guide/#watchman for more info.
Building
'instrument' is imported from external module 'ember-data/-debug' but never used
/usr/local/bin/node[1116]: ../src/node_file.cc:598:void node::InternalModuleReadFile(const v8::FunctionCallbackInfo<v8::Value>&): Assertion `(numchars) >= (0)' failed.
fs.js:682
  var r = binding.read(fd, buffer, offset, length, position);
                  ^

Error: ENOMEM: not enough memory, read
    at Object.fs.readSync (fs.js:682:19)
    at tryReadSync (fs.js:480:20)
    at Object.fs.readFileSync (fs.js:509:19)
    at Object.Module._extensions..js (module.js:579:20)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at /home/circleci/app/node_modules/esutils/lib/utils.js:31:23
fs.js:682
  var r = binding.read(fd, buffer, offset, length, position);
                  ^

Error: ENOMEM: not enough memory, read
    at Object.fs.readSync (fs.js:682:19)
    at tryReadSync (fs.js:480:20)
    at Object.fs.readFileSync (fs.js:509:19)
    at Object.Module._extensions..js (module.js:579:20)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/home/circleci/app/node_modules/ember-power-select/node_modules/babel-core/lib/transformation/transformers/index.js:43:22)
fs.js:682
  var r = binding.read(fd, buffer, offset, length, position);
                  ^

Error: ENOMEM: not enough memory, read
    at Object.fs.readSync (fs.js:682:19)
    at tryReadSync (fs.js:480:20)
    at Object.fs.readFileSync (fs.js:509:19)
    at Object.Module._extensions..js (module.js:579:20)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/home/circleci/app/node_modules/debug/src/node.js:14:28)
 1: node::Abort() [/usr/local/bin/node]
 2: node::Assert(char const* const (*) [4]) [/usr/local/bin/node]
 3: 0x12e49fa [/usr/local/bin/node]
 4: v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) [/usr/local/bin/node]
 5: 0xb45e2c [/usr/local/bin/node]
 6: v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/local/bin/node]
 7: 0x1cbf812040c7
fs.js:682
  var r = binding.read(fd, buffer, offset, length, position);
                  ^

Error: ENOMEM: not enough memory, read
    at Object.fs.readSync (fs.js:682:19)
    at tryReadSync (fs.js:480:20)
    at Object.fs.readFileSync (fs.js:509:19)
    at Object.Module._extensions..js (module.js:579:20)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/home/circleci/app/node_modules/debug/src/node.js:14:28)
fs.js:682
  var r = binding.read(fd, buffer, offset, length, position);
                  ^

Error: ENOMEM: not enough memory, read
    at Object.fs.readSync (fs.js:682:19)
    at tryReadSync (fs.js:480:20)
    at Object.fs.readFileSync (fs.js:509:19)
    at Object.Module._extensions..js (module.js:579:20)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/home/circleci/app/node_modules/regenerator/node_modules/ast-types/lib/node-path.js:6:12)
cleaning up
cleaning up...
Build failed.
The Broccoli Plugin: [BroccoliMergeTrees: Addon#treeFor (ember-concurrency - addon)] failed with:
Error: Worker terminated unexpectedly
    at ChildProcess.<anonymous> (/home/circleci/app/node_modules/workerpool/lib/WorkerHandler.js:177:17)
    at emitTwo (events.js:106:13)
    at ChildProcess.emit (events.js:194:7)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:215:12)

The broccoli plugin was instantiated at: 
    at BroccoliMergeTrees.Plugin (/home/circleci/app/node_modules/broccoli-plugin/index.js:7:31)
    at new BroccoliMergeTrees (/home/circleci/app/node_modules/broccoli-merge-trees/index.js:16:10)
    at Function.BroccoliMergeTrees [as _upstreamMergeTrees] (/home/circleci/app/node_modules/broccoli-merge-trees/index.js:10:53)
    at mergeTrees (/home/circleci/app/node_modules/ember-cli/lib/broccoli/merge-trees.js:85:33)
    at Class.treeFor (/home/circleci/app/node_modules/ember-cli/lib/models/addon.js:526:30)
    at addons.reduce (/home/circleci/app/node_modules/ember-cli/lib/models/addon.js:383:26)
    at Array.reduce (native)
    at Class.eachAddonInvoke (/home/circleci/app/node_modules/ember-cli/lib/models/addon.js:380:24)
    at Class.treeFor (/home/circleci/app/node_modules/ember-cli/lib/models/addon.js:515:22)
    at project.addons.reduce (/home/circleci/app/node_modules/ember-cli/lib/broccoli/ember-app.js:559:25)


Exited with code 1

La solución "Workaround", proporcionada por @rwjblue , es definir específicamente la cantidad de trabajos que desea usar para la transpilación paralela aprovechando la JOBS ENV var. (https://github.com/mwisner/ember-circleci-example/blob/09c63e11c34d4cdfe602b63166b71e6f31e30f3c/.circleci/config.yml#L42)

Has Reproduction

Comentario más útil

Hablé un poco con @mwisner y @kiwiupover sobre esto durante el fin de semana y descubrimos que tiene que ver con el nuevo paralelismo que se agregó a broccoli-babel-transpiler. Por defecto se paraleliza al número de CPU actualmente presentes. Desafortunadamente, en CircleCI esto se muestra como 36 CPU, pero el trabajo en sí está limitado a 2 procesos simultáneos (y también limitado en RAM disponible).

La solución aquí es establecer la variable de entorno JOBS en 1 para deshabilitar esencialmente el paralelismo en CI.

Todos 23 comentarios

Parece que hay algunas personas que tienen pruebas de karma trabajando con este paso:

https://discuss.circleci.com/t/running-browser-tests/10998/9

Después de las últimas horas, aún no he podido hacer que esto funcione con éxito, pero estoy bastante seguro de que esto no es específicamente ember.js... Cerrando por ahora.

Tengo este mismo problema y no he podido resolverlo.

Hablé un poco con @mwisner y @kiwiupover sobre esto durante el fin de semana y descubrimos que tiene que ver con el nuevo paralelismo que se agregó a broccoli-babel-transpiler. Por defecto se paraleliza al número de CPU actualmente presentes. Desafortunadamente, en CircleCI esto se muestra como 36 CPU, pero el trabajo en sí está limitado a 2 procesos simultáneos (y también limitado en RAM disponible).

La solución aquí es establecer la variable de entorno JOBS en 1 para deshabilitar esencialmente el paralelismo en CI.

@rwjblue gracias por el contexto aquí!

Incluso con el paralelismo de Circle, pensaría (porque están divididos en contenedores) que JOBS siempre tendría que ser 1, entonces. Entonces por...

esencialmente deshabilitar el paralelismo en CI

...Supongo que quiere desactivar el paralelismo broccoli-babel-transpiler y no el paralelismo en contenedores de Circle. ¿Está bien?

@ eric-hu, ¿esto también te parece una lectura correcta? ¿Es algo de lo que deberíamos esperar una solución de Circle para que se muestren las CPU realmente disponibles en el trabajo? ¿Evita esto cualquier tipo de paralelismo por parte de Circle (dividiendo las pruebas con algo como ember-exam por ejemplo)?

No estoy seguro de si podemos esperar una solución de circleci ellos mismos ... No estoy súper al 100%, pero creo que es más un problema de Docker que un problema de círculo específicamente. O tal vez un problema de Node + Docker que no puede detectar con precisión las limitaciones de cpu/mem impuestas por el contenedor docker.

Creé este repositorio con fines de experimentación: https://github.com/mwisner/ember-circleci-example.

Incluye circleci 2.0 (funciona con la solución alternativa JOBS=1 proporcionada por @rwjblue) (https://github.com/mwisner/ember-circleci-example/blob/master/.circleci/config.yml)

Junto con las compilaciones del círculo público: https://circleci.com/gh/mwisner/ember-circleci-example/83

Sin embargo, también agregué el repositorio a travis ci, que también usa Docker para compilaciones. Todavía no he arreglado el archivo de configuración de travis, pero puede ver que las compilaciones de travis están fallando con la configuración de travis provista (https://travis- ci.org/mwisner/ember-circleci-example/builds)

Gracias @mwisner. ¿Estaba en lo cierto al pensar que JOBS=1 _no_ significa que no podemos paralelizar las compilaciones en sí mismas?

@JoshSmith Sí, si entiendo correctamente, es para deshabilitar el paralelismo dentro de broccoli-babel-transpiler. No circular en sí mismo. Entonces, en teoría, usar el paralelismo circleci + trabajos = 1 estaría bien.

Pero personalmente, no he experimentado con el uso de la funcionalidad de paralelismo de circleci. Así que no estoy 100% seguro de cómo se vería un archivo de configuración para eso.

¡Tiene sentido! Quería estar seguro de que estaba dibujando una línea clara entre lo que parecían dos usos distintos de "paralelismo" aquí, así que creo que finalmente estoy en la misma página.

Me di cuenta de que estaba configurando JOBS=1 manualmente en la configuración antes de ember test , pero parece que eso podría configurarse en el nivel de ENV var dentro de Circle, ¿quizás sin problemas?

Tampoco es específico de las pruebas. El problema de OOM se produce durante la fase de compilación de la prueba de brasa. También probé ejecutando ember build con el mismo resultado OOM.

@JoshSmith Creo que establecer JOBS=1 en el nivel de env var también funcionaría, pero no lo he confirmado.

Me di cuenta de que hay un patrón para configurar env vars para los comandos ember cli para algunos complementos:

https://github.com/ember-cli/broccoli-viz#usage
https://github.com/kategengler/ember-cli-code-coverage#usage

Así que me estaba saliendo de esos patrones de uso.

Sin saber _nada_ sobre cómo funciona realmente broccoli-babel-transpiler , es difícil para mí decir si Circle (o Docker, o quien sea) podría proporcionar una solución. El _sentimiento_ de mi profano, no el pensamiento, en torno a esto sería que si el transpilador pudiera tomar instrucciones explícitas sobre cuántos núcleos hay disponibles, entonces tal vez podríamos evitar el problema de la inferencia pura aquí. De nuevo, esto viene de un lugar de profunda ignorancia.

Solo para actualizar, puedo ejecutar estas compilaciones correctamente con JOBS establecido en 1 en la configuración ambiental de Circle. Gracias de nuevo @rwjblue , @mwisner y @eric-hu.

Acabo de encontrarme con este mismo problema en CircleCI, y JOBS=1 también me lo arregló, gracias a todos :v:

@ eric-hu, ¿esto también te parece una lectura correcta? ¿Es algo de lo que deberíamos esperar una solución de Circle para que se muestren las CPU realmente disponibles en el trabajo? ¿Evita esto algún tipo de paralelismo por parte de Circle (por ejemplo, dividiendo las pruebas con algo como un examen de ascuas)?

@JoshSmith hay dos conceptos de paralelismo a tener en cuenta para CircleCI 2.0:

A. Paralelismo por comando, limitado a la cantidad de núcleos disponibles para un grupo de contenedores. De forma predeterminada, a cada grupo de contenedores se le asignan 2 recursos compartidos de CPU, lo que garantiza que obtengan 2 núcleos de CPU. Hay una función de recursos configurables premium que le permite elegir una parte más grande o más pequeña (1, 4, 8 de la parte superior de mi cabeza).

B. Paralelismo CircleCI, que puede pensar como "¿en cuántas máquinas [1] quiero dividir esto?". Esto es útil para el aislamiento de pruebas, cuando desee ejecutar 2 pruebas al mismo tiempo y ambas escriban en una base de datos. Esto es menos útil para, por ejemplo, transpilar sus activos; probablemente desee que todas sus pruebas se ejecuten con los activos transpilados.

Con respecto a A: dado que tiene 2 núcleos garantizados disponibles de forma predeterminada, es posible que pueda ejecutar el comando que desee con JOBS = 2. Esto puede acelerar la ejecución, pero no he comprobado si funciona.

Con respecto a B: incluso con JOBS = 1, aún puede usar el paralelismo CircleCI para acelerar su conjunto de pruebas.

Con respecto a "quién debería arreglar esto", lo he visto como un problema de larga data con múltiples herramientas de creación de contenedores. Las herramientas de creación de contenedores CircleCI 2.0 y 1.0 --Docker y LXC respectivamente-- filtran información sobre el sistema host para muchos comandos comunes de Linux, como los que se usan para verificar cuántos núcleos están disponibles. Ha sido así durante varios años, creo que si hubiera una solución simple, ya se habría resuelto. Para complicar aún más las cosas, CircleCI cambió el modelo de disponibilidad del núcleo de la CPU de 1.0 a 2.0. En 1.0, tenía un número fijo de núcleos para un trabajo. En 2.0, se le asignan cuotas de CPU para garantizar su número mínimo de núcleos. Si está en un host completamente utilizado, obtendrá al menos esa cantidad de núcleos. Si está ejecutando en un host infrautilizado, tendrá más núcleos disponibles para usted. Sin embargo, herramientas como broccoli-babel-transpiler tienden a asignar la cantidad de núcleos que usarán solo una vez, y los recursos disponibles pueden cambiar durante la vida útil de la ejecución del programa. Lo mejor es simplemente codificar sus scripts de CI para usar los recursos garantizados disponibles.

[1] Es posible que su código no se ejecute en N máquinas para N paralelismo. Pero puedes pensarlo de esta manera, ya que están efectivamente aislados el uno del otro.

Acabo de probar JOBS=2 en mi aplicación y la compilación también fue exitosa. ¿Debería ser ese el consejo canónico aquí?

No hay una diferencia de tiempo notable entre esos valores en mi pequeña muestra FWIW.

@rwjblue Sé que recomendó tener este problema en el repositorio ember-cli. Sin embargo, abrí este problema cuando lo descubrí por primera vez y parece que se encontró antes de que pudiera abrir otro en el repositorio ember-cli... ¿Le gustaría que abriera otro problema allí y solo hiciera referencia a esta conversación? ¿Conoces alguna forma de moverlo fácilmente?

Reabrí este problema porque después de realizar algunas pruebas adicionales, la configuración travisci.yml provista que se envía con Ember también se encuentra con este problema.

Si bien entiendo que Ember no es compatible con Circle, creo que sería bueno al menos solucionar el problema en el archivo travisci.yml enviado.

También actualicé el título y la descripción para que sea un poco más genérico y que no tenga un alcance específico para circleCI.

@ eric-hu muchas gracias por los consejos detallados aquí. Muy útil para que la comunidad entienda lo que está pasando en detalle. Sería genial ver un ejemplo canónico en la documentación por marco, aunque entiendo y agradezco el tiempo que tomaría.

@bgentry gracias por informar sobre la diferencia horaria. Tenía la esperanza de que aceleraría los tiempos de construcción. También voy a configurar JOBS=2 , pero estoy un poco decepcionado por esto, ya que mis tiempos de compilación son, con mucho, mi mayor obstáculo para acelerar los trabajos de Circle.

oh wow, me alegro de que finalmente encontré esta conversación, porque esto también me ha estado pasando a mí. No sabía qué buscar al principio porque npm test se estaba agotando para mí en Travis, sin comentarios. No fue hasta que intenté anular los tiempos de espera (que normalmente tendrías que escribir para que lo haga el soporte) que obtuve un ENOMEM , que finalmente me llevó aquí.

Cambiar a JOBS=2 npm test ha provocado que mis compilaciones se aprueben nuevamente (o al menos fallen por las razones correctas 😆), ¡así que gracias a todos!

Tal vez esto solo me sucedía en Travis porque la aplicación tiene muchas dependencias, pero fue difícil de depurar y simplemente la ignoré durante mucho tiempo, por lo que parece que vale la pena considerar cómo manejar esto en el plano de Ember CLI o de otra manera. eso.

En mi prueba, un proyecto ember predeterminado listo para usar sin ningún cambio funciona bien, la introducción de un montón de dependencias termina causando el error.

No estoy seguro de lo que se considera un "mucho" de dependencias en un proyecto de Ember. Pero dado que travis es la forma de facto de hacer IC con complementos de Ember, creo que a medida que las personas comiencen a actualizar / crear nuevos complementos, la gente verá esto cada vez más.

Recientemente comencé a trabajar en la actualización de todas las dependencias para el proyecto ember-burger-menu y recibo este error.
Ejemplo:
https://travis-ci.org/offirgolan/ember-burger-menu/builds/275031562?utm_source=github_status&utm_medium=notification
https://github.com/offirgolan/ember-burger-menu/pull/95

¡Oh! ¡Gracias @mwisner por publicar este problema y @rwjblue por la solución temporal! Acabo de pasar unas horas tratando de entender por qué fallan mis compilaciones. Establecer TRABAJOS en 1 es el truco 🎉

Cerrar como JOBS=1 se actualizó como predeterminado en ember-cli hace un tiempo. Lo siento por los problemas...

Solo tuve que agregar JOBS=1 para solucionar este problema en 3.16.0. ¿Hubo una regresión?

¿Fue útil esta página
0 / 5 - 0 calificaciones