programming languages - languages - ¿Existe un lenguaje a prueba de desastres?
programming languages java (28)
Sin embargo, su característica principal debería ser: si el poder muere, y la cosa se reinicia toma de donde lo dejó (así que no solo recuerda dónde estaba, sino que también recordará los estados variables). Además, si se detuvo en medio de una copia de archivo, también se reanudará correctamente. etcétera etcétera.
...
He mirado erlang en el pasado. Por muy agradables que sean las características tolerantes a fallas que tiene ... No sobrevive a un powercut. Cuando el código se reinicie tendrás que recoger las piezas.
Si existiera tal tecnología, estaría MUY interesado en leer sobre ella. Dicho esto, la solución de Erlang tendría múltiples nodos, idealmente en diferentes ubicaciones, de modo que si una ubicación fallaba, los otros nodos podrían recuperarse. Si todos sus nodos estuvieran en la misma ubicación y en la misma fuente de energía (no es una buena idea para sistemas distribuidos), entonces no tendrá suerte como mencionó en un seguimiento de comentarios.
Al crear servicios del sistema que deben tener una alta confiabilidad, a menudo termino escribiendo muchos mecanismos "a prueba de fallos" en el caso de cosas como: las comunicaciones que se han ido (por ejemplo, la comunicación con el DB), ¿qué pasaría si el poder es Perdió y el servicio se reinicia ... cómo recoger las piezas y continuar de forma correcta (y recordar que al recoger las piezas, la energía podría volver a salir ...), etc., etc.
Me imagino que para sistemas no demasiado complejos, un lenguaje que cubra esto sería muy práctico. Por lo tanto, un lenguaje que recordaría su estado en un momento dado, sin importar si se corta la alimentación, y continúa donde lo dejó.
¿Ya existe esto? Si es así, ¿dónde puedo encontrarlo? Si no, ¿por qué no se puede realizar esto? Me parecería muy útil para sistemas críticos.
ps En caso de que se pierda la conexión DB, esto indicaría que surgió un problema y que se necesita una intervención manual. En el momento en que se restablezca la conexión, continuará donde se detuvo.
EDITAR: Ya que la discusión parece haberse extinguido, permítame agregar algunos puntos (mientras espero antes de poder agregar una recompensa a la pregunta)
La respuesta de Erlang parece ser la más alta en este momento. Soy consciente de Erlang y he leído el libro pragmático de Armstrong (el creador principal). Todo es muy bonito (aunque los lenguajes funcionales hacen que mi cabeza gire con toda la recursión), pero el bit de "tolerancia a fallos" no viene automáticamente. Lejos de ahi. Erlang ofrece una gran cantidad de supervisores y otras metodologías para supervisar un proceso y reiniciarlo si es necesario. Sin embargo, para hacer correctamente algo que funcione con estas estructuras, debe ser un gran gurú de Erlang y debe hacer que su software se ajuste a todos estos marcos. Además, si la potencia cae, el programador también tiene que recoger las piezas e intentar recuperar la próxima vez que se reinicie el programa.
Lo que estoy buscando es algo mucho más simple:
Imagine un lenguaje (tan simple como PHP por ejemplo), donde puede hacer cosas como hacer consultas de base de datos, actuar sobre él, realizar manipulaciones de archivos, realizar manipulaciones de carpetas, etc.
Sin embargo, su característica principal debería ser: si el poder muere, y la cosa se reinicia toma de donde lo dejó (así que no solo recuerda dónde estaba, sino que también recordará los estados variables). Además, si se detuvo en medio de una copia de archivo, también se reanudará correctamente. etcétera etcétera.
Por último, pero no menos importante, si la conexión de la base de datos se interrumpe y no se puede restaurar, el idioma se detiene y las señales (quizás syslog) para la intervención humana, y luego continúan donde se detuvieron.
Un lenguaje como este haría mucho más fácil la programación de muchos servicios.
EDITAR: Parece (a juzgar por todos los comentarios y respuestas) que tal sistema no existe. Y probablemente no lo hará en un futuro cercano debido a que es (¿cerca?) Imposible de hacer bien.
Lástima ... otra vez, no estoy buscando este lenguaje (o marco) para llevarme a la luna, ni lo uso para monitorear el ritmo cardíaco de alguien. Pero para pequeños servicios / tareas periódicas que siempre terminan teniendo un montón de límites de manejo de código (falla de energía en algún punto intermedio, las conexiones se caen y no vuelven a activarse), ... donde hay una pausa aquí, ... solucione los problemas, ..y continuar donde dejaste el enfoque funcionaría bien.
(o un enfoque de punto de control como señaló uno de los comentaristas (como en un videojuego). Establezca un punto de control ... y si el programa muere, reinicie aquí la próxima vez).
Recompensa otorgada: En el último minuto posible, cuando todos llegaron a la conclusión de que no se puede hacer, Stephen C viene con napier88, que parece tener los atributos que estaba buscando. Aunque es un lenguaje experimental, prueba que se puede hacer y es algo que vale la pena investigar más.
Estaré buscando crear mi propio marco (con estado persistente e instantáneas quizás) para agregar las funciones que estoy buscando en .Net u otra máquina virtual.
Todos gracias por el aporte y las grandes ideas.
Considere un sistema construido a partir de memoria no volátil. El estado del programa se mantiene en todo momento, y si el procesador se detiene durante un período de tiempo prolongado, se reanudará en el punto que dejó cuando se reinicia. Por lo tanto, su programa es ''a prueba de desastres'' en la medida en que puede sobrevivir a un fallo de alimentación.
Esto es completamente posible, como lo han señalado otras publicaciones al hablar de Software Transactional Memory, ''tolerancia a fallos'', etc. Curioso, nadie mencionó ''memristores'', ya que ofrecerían una arquitectura futura con estas propiedades y una que quizás no sea completamente von Neumann La arquitectura también.
Ahora imagine un sistema construido a partir de dos de estos sistemas discretos: para una ilustración sencilla, uno es un servidor de base de datos y el otro un servidor de aplicaciones para un sitio web de banca en línea.
Si uno hace una pausa, ¿qué hace el otro? ¿Cómo maneja la falta de disponibilidad repentina de su compañero de trabajo?
Podría manejarse a nivel de idioma, pero eso significaría un montón de manejo de errores y eso es un código complicado para corregirlo. Eso no es mucho mejor que donde estamos hoy, donde las máquinas no están controladas pero los idiomas intentan detectar problemas y pedirle al programador que se ocupe de ellos.
También podría hacer una pausa: en el nivel de hardware, podrían estar vinculados, de modo que desde una perspectiva de poder son un solo sistema. Pero eso no es una buena idea; una mejor disponibilidad provendría de una arquitectura tolerante a fallas con sistemas de respaldo y tal.
O podríamos usar colas de mensajes persistentes entre las dos máquinas. Sin embargo, en algún momento estos mensajes se procesan, ¡y en ese momento pueden ser demasiado antiguos! Solo la lógica de la aplicación puede funcionar realmente qué hacer en esas circunstancias, y allí estamos de nuevo a los idiomas delegando al programador nuevamente.
Por lo tanto, parece que la prueba de desastres es mejor en la forma actual: fuentes de alimentación ininterrumpidas, servidores de respaldo listos para usar, múltiples rutas de red entre hosts, etc. ¡Y solo tenemos que esperar que nuestro software esté libre de errores!
Creo que es un error fundamental para la recuperación no ser un problema de diseño destacado. La imputación exclusiva de la responsabilidad al medio ambiente conduce a una solución generalmente frágil e intolerante a las fallas internas.
Si fuera yo, invertiría en hardware confiable Y diseñaría el software de manera que pudiera recuperarse automáticamente de cualquier posible condición. Según su ejemplo, el mantenimiento de la sesión de la base de datos debe manejarse automáticamente mediante una API de nivel suficientemente alto. Si tiene que volver a conectarse manualmente, es probable que esté utilizando la API incorrecta.
Como otros han señalado, los lenguajes de procedimientos integrados en los sistemas RDBMS modernos son los mejores que obtendrá sin el uso de un lenguaje exótico.
Las máquinas virtuales en general están diseñadas para este tipo de cosas. Puede usar una API de proveedores de VM (vmware..et al) para controlar los puntos de control periódicos dentro de su aplicación, según corresponda.
VMWare, en particular, tiene una función de reproducción (Registro de Ejecución Mejorada) que registra TODO y permite la reproducción en un punto en el tiempo. Obviamente, hay un impacto de rendimiento masivo con este enfoque, pero cumpliría con los requisitos. Simplemente me aseguraría de que sus unidades de disco tengan una memoria caché de escritura respaldada por batería.
Es muy probable que pueda encontrar soluciones similares para el código de bytes de Java ejecutado dentro de una máquina virtual de Java. JVM tolerante a fallos de Google y punto de control de la máquina virtual.
Dependiendo de su definición de desastre, puede ir desde ''difícil'' hasta ''prácticamente imposible'' para delegar esta responsabilidad al idioma.
Otros ejemplos que se dan incluyen la persistencia del estado actual de la aplicación a NVRAM después de ejecutar cada instrucción. Esto solo funciona mientras la computadora no se destruya.
¿Cómo sabría una función de nivel de idioma para reiniciar la aplicación en un nuevo host?
Y en la situación de restauración de la aplicación a un host, ¿qué pasaría si hubiera pasado un tiempo significativo y las suposiciones / verificaciones realizadas anteriormente fueran ahora inválidas?
T-SQL, PL / SQL y otros lenguajes transaccionales son probablemente lo más cercanos a "prueba de desastres": tienen éxito (y los datos se guardan) o no. Excluyendo la desactivación del aislamiento transaccional, es difícil (pero probablemente no imposible si realmente se esfuerza) entrar en estados "desconocidos".
Puede usar técnicas como la creación de reflejo SQL para asegurarse de que las escrituras se guarden en al menos dos ubicaciones al mismo tiempo antes de que se confirme una transacción.
Aún debe asegurarse de guardar su estado cada vez que sea seguro (cometer).
Dudo que las características del lenguaje que estás describiendo sean posibles de lograr.
Y la razón es que sería muy difícil definir los modos de falla comunes y generales y cómo recuperarse de ellos. Piense por un segundo acerca de su aplicación de muestra: algún sitio web con cierta lógica y acceso a bases de datos. Y digamos que tenemos un lenguaje que puede detectar el apagado de energía y el reinicio posterior, y de alguna manera recuperarnos de él. El problema es que es imposible saber por el lenguaje cómo recuperarse.
Digamos que su aplicación es una aplicación de blog en línea. En ese caso, podría ser suficiente simplemente continuar desde el punto en el que fallamos y todo está bien. Sin embargo, considere un escenario similar para un banco en línea. De repente, ya no es inteligente simplemente continuar desde el mismo punto. Por ejemplo, si intentaba retirar algo de dinero de mi cuenta y la computadora murió justo después de los cheques, pero antes de realizar el retiro, y luego una semana más tarde, me dará el dinero aunque mi cuenta esté en el negativo ahora
En otras palabras, no existe una única estrategia de recuperación correcta, por lo que esto no es algo que pueda implementarse en el lenguaje. Lo que el lenguaje puede hacer es decirle cuándo sucede algo malo, pero la mayoría de los idiomas ya lo admiten con mecanismos de manejo de excepciones. El resto depende de los diseñadores de aplicaciones para pensar.
Existen muchas tecnologías que permiten diseñar aplicaciones tolerantes a fallas. Transacciones de base de datos, colas de mensajes duraderos, agrupación en clústeres, intercambio en caliente de hardware, etc. Pero todo depende de los requisitos concretos y de cuánto esté dispuesto a pagar el usuario final.
El Grupo de robótica de Microsoft ha introducido un conjunto de bibliotecas que parecen ser aplicables a su pregunta.
¿Qué es el tiempo de ejecución de concurrencia y coordinación (CCR)?
El Tiempo de Ejecución de Concurrencia y Coordinación (CCR) proporciona un modelo de programación altamente concurrente basado en el paso de mensajes con potentes primitivas de orquestación que permiten la coordinación de datos y trabajo sin el uso de subprocesos manuales, bloqueos, semáforos, etc. aplicaciones concurrentes al proporcionar un modelo de programación que facilita la administración de operaciones asíncronas, el manejo de la concurrencia, la explotación de hardware paralelo y el manejo de fallas parciales.
¿Qué es Servicios de Software Descentralizado (DSS)?
Los servicios de software descentralizado (DSS) proporcionan un modelo de servicio ligero orientado al estado que combina la transferencia de estado representacional (REST) con una composición formalizada y una arquitectura de notificación de eventos que permite un enfoque a nivel de sistema para crear aplicaciones. En DSS, los servicios se exponen como recursos a los que se puede acceder mediante programación y para la manipulación de la interfaz de usuario. Al integrar la composición del servicio, la manipulación del estado estructurado y la notificación de eventos con el aislamiento de datos, DSS proporciona un modelo uniforme para escribir aplicaciones altamente observables y poco acopladas que se ejecutan en un solo nodo o en toda la red.
La mayoría de las respuestas dadas son lenguajes de propósito general. Es posible que desee buscar idiomas más especializados que se utilizan en dispositivos integrados. El robot es un buen ejemplo para pensar. ¿Qué desearía y / o esperaría que hiciera un robot cuando se recuperara de un fallo de alimentación?
En el caso de una falla de energía ... me suena como: "Cuando tu única herramienta es un martillo, cada problema parece un clavo"
No solucionas problemas de falla de energía dentro de un programa. Usted resuelve este problema con fuentes de alimentación de respaldo, baterías, etc.
En el mundo integrado, esto se puede implementar a través de una interrupción de vigilancia y una memoria RAM con respaldo de batería. Yo mismo he escrito eso.
En mi opinión, el concepto de recuperación de fallas es, en la mayoría de los casos, un problema comercial, no un problema de hardware o de idioma.
Tomemos un ejemplo: usted tiene un nivel UI y un subsistema. El subsistema no es muy confiable, pero el cliente en el nivel de UI debería percibirlo como si lo fuera.
Ahora, imagine que de alguna manera se bloquea su subsistema, ¿realmente cree que el lenguaje que imagina puede pensar por usted cómo manejar el Nivel de UI según este subsistema?
Su usuario debe ser explícitamente consciente de que el subsistema no es confiable, si utiliza la mensajería para proporcionar una alta confiabilidad, el cliente DEBE saberlo (si no lo sabe, la IU puede congelarse esperando una respuesta que puede llegar 2 semanas después) ). Si él debe ser consciente de esto, esto significa que cualquier abstracción para ocultarlo eventualmente se perderá.
Por cliente, me refiero al usuario final. Y la interfaz de usuario debe reflejar esta falta de fiabilidad y no ocultarla; en ese caso, una computadora no puede pensar por usted.
Esta pregunta me obligó a publicar este texto.
(Se cita de HGTTG de Douglas Adams :)
Haga clic, hum.
La enorme nave gris de reconocimiento Grebulon se movió silenciosamente a través del vacío negro. Viajaba a una velocidad fabulosa e impresionante, pero apareció, contra el resplandeciente fondo de mil millones de estrellas distantes que no se movían en absoluto. Era solo una mancha oscura congelada contra una granularidad infinita de noche brillante.
A bordo de la nave, todo fue como había sido durante milenios, profundamente oscuro y silencioso.
Haga clic, hum.
Al menos, casi todo.
Haga clic, haga clic, zumbido.
Haga clic, zumbido, haga clic, zumbido, haga clic, zumbido.
Haz clic, haz clic, haz clic, haz clic, haz clic, zumbido.
Hmmm
Un programa de supervisión de bajo nivel despertó un programa de supervisión de nivel ligeramente más profundo en el ciberbaceno semi-somnolente de la nave y le informó que cada vez que hacía clic, todo lo que recibía era un zumbido.
El programa de supervisión de nivel superior le preguntó qué se suponía que debía obtener, y el programa de supervisión de bajo nivel dijo que no podía recordar exactamente, pero pensó que probablemente era más bien una especie de suspiro de satisfacción distante, ¿no? No sabía qué era este zumbido. Haga clic, hum, haga clic, hum. Eso fue todo lo que estaba recibiendo.
El programa de supervisión de nivel superior consideró esto y no le gustó. Preguntó al programa de supervisión de bajo nivel qué era exactamente la supervisión y el programa de supervisión de bajo nivel dijo que tampoco podía recordar eso, solo que era algo que estaba destinado a hacer clic, suspirar cada diez años aproximadamente, lo que generalmente ocurría sin fallar. Intentó consultar su tabla de búsqueda de errores, pero no pudo encontrarla, por lo que alertó al programa de supervisión de nivel superior sobre el problema.
El programa de supervisión de nivel superior fue a consultar una de sus propias tablas de consulta para averiguar qué se suponía que el programa de supervisión de bajo nivel debía supervisar.
No se pudo encontrar la tabla de consulta.
Impar.
Miró de nuevo. Todo lo que consiguió fue un mensaje de error. Intentó buscar el mensaje de error en su tabla de consulta de mensajes de error y tampoco pudo encontrarlo. Permitió que pasaran un par de nanosegundos mientras volvía a pasar por todo esto. Luego despertó su función sectorial de supervisor.
El supervisor de la función del sector golpeó problemas inmediatos. Llamó a su agente de supervisión que también afectó a los problemas. Dentro de unas pocas millonésimas de segundo circuitos virtuales que habían permanecido inactivos, algunos por años, algunos por siglos, cobraron vida en todo el barco. Algo, en alguna parte, había salido terriblemente mal, pero ninguno de los programas de supervisión podía decir qué era. En todos los niveles, faltaban instrucciones vitales y faltaban las instrucciones sobre qué hacer en caso de descubrir que faltaban instrucciones vitales.
Pequeños módulos de software - agentes - surgieron a través de las vías lógicas, agrupando, consultando, reagrupando. Rápidamente establecieron que la memoria de la nave, todo el camino de regreso a su módulo de misión central, estaba hecha jirones. Ninguna cantidad de interrogación podía determinar qué era lo que había sucedido. Incluso el módulo central de la misión parecía estar dañado.
Esto hizo que todo el problema fuera muy fácil de tratar. Reemplace el módulo de la misión central. Había otro, una copia de seguridad, un duplicado exacto del original. Tuvo que ser reemplazado físicamente porque, por razones de seguridad, no había ningún vínculo entre el original y su copia de seguridad. Una vez que el módulo de la misión central fuera reemplazado, él mismo podría supervisar la reconstrucción del resto del sistema en cada detalle, y todo estaría bien.
Se instruyó a los robots para que trajeran el módulo central de respaldo de la misión desde la fuerte sala blindada, donde lo guardaban, a la cámara lógica de la nave para su instalación.
Esto implicó el largo intercambio de códigos y protocolos de emergencia cuando los robots interrogaron a los agentes sobre la autenticidad de las instrucciones. Por fin los robots estaban convencidos de que todos los procedimientos eran correctos. Desempacaron el módulo de la misión central de respaldo de su alojamiento de almacenamiento, lo sacaron de la cámara de almacenamiento, se cayeron de la nave y se fueron girando hacia el vacío.
Esto proporcionó la primera pista importante en cuanto a qué era lo que estaba mal.
Más investigación rápidamente estableció qué fue lo que había sucedido. Un meteorito había hecho un gran agujero en el barco. La nave no había detectado esto previamente porque el meteorito había eliminado cuidadosamente esa parte del equipo de procesamiento de la nave que se suponía detectaba si la nave había sido alcanzada por un meteorito.
Lo primero que debía hacer era tratar de sellar el agujero. Esto resultó imposible, ya que los sensores de la nave no podían ver que había un agujero, y los supervisores que deberían haber dicho que los sensores no funcionaban correctamente no funcionaban correctamente y continuaron diciendo que los sensores estaban bien. La nave solo podía deducir la existencia del agujero a partir del hecho de que los robots se habían caído claramente de él, llevándose su cerebro de repuesto, lo que le habría permitido ver el agujero, con ellos.
La nave intentó pensar inteligentemente acerca de esto, falló y luego se quedó completamente en blanco por un momento. No se dio cuenta de que se había borrado, por supuesto, porque se había borrado. Simplemente se sorprendió al ver las estrellas saltar. Después de la tercera vez que las estrellas saltaron, la nave finalmente se dio cuenta de que debía estar en blanco, y que era hora de tomar algunas decisiones serias.
Se relajó.
Entonces se dio cuenta de que aún no había tomado las decisiones serias y se había asustado. Se volvió a borrar por un momento. Cuando volvió a despertarse, selló todos los mamparos alrededor de donde sabía que debía estar el agujero invisible.
Claramente, aún no había llegado a su destino, pensó, pero ya que ya no tenía la menor idea de dónde estaba su destino o cómo llegar a él, parecía que no tenía mucho sentido continuar. Consultó qué pequeños fragmentos de instrucciones podía reconstruir a partir de los jirones de su módulo de misión central.
"¡¡¡Tu !!!!! !!!!! !!!!! misión del año es !!!!! !!!!! !!!!! !!!!!, !!!!! !!! ¡¡¡¡¡¡¡¡!!!!!!! .. ..... ...., land ..... ..... ..... monitorearlo. !!!!! !!!!! !!!!! ... "
Todo el resto fue basura completa.
Antes de que se borrara definitivamente, la nave tendría que transmitir esas instrucciones, como eran, a sus sistemas subsidiarios más primitivos.
También debe revivir a toda su tripulación.
Había otro problema. Mientras la tripulación estaba en hibernación, las mentes de todos sus miembros, sus recuerdos, sus identidades y su comprensión de lo que habían venido a hacer, se habían transferido al módulo de la misión central de la nave para su custodia. La tripulación no tendría la menor idea de quiénes eran o qué estaban haciendo allí. Oh bien.
Justo antes de que quedara en blanco por última vez, la nave se dio cuenta de que sus motores también estaban empezando a apagarse.
El barco y su tripulación revivida y confundida se deslizaron bajo el control de sus sistemas automáticos subsidiarios, que simplemente buscaban aterrizar donde pudieran encontrar para aterrizar y monitorear lo que pudieran encontrar para monitorear.
Existe un lenguaje experimental llamado Napier88 que (en teoría) tiene algunos atributos de ser a prueba de desastres. El lenguaje admite la persistencia ortogonal, y en algunas implementaciones esto se extiende (extendido) para incluir el estado de todo el cálculo. Específicamente, cuando el sistema de tiempo de ejecución Napier88 verificó una aplicación en ejecución al almacén persistente, el estado del hilo actual se incluiría en el punto de control. Si la aplicación se bloqueaba y la reiniciaba de la manera correcta, podría reanudar el cálculo desde el punto de control.
Desafortunadamente, hay una serie de problemas difíciles que deben abordarse antes de que este tipo de tecnología esté lista para el uso general. Éstas incluyen descubrir cómo admitir subprocesos múltiples en el contexto de la persistencia ortogonal, descubrir cómo permitir que múltiples procesos compartan un almacén persistente y una recolección de basura escalable de almacenes persistentes.
Y existe el problema de hacer la persistencia ortogonal en un lenguaje general. Ha habido intentos de hacer OP en Java, incluyendo uno que fue realizado por personas asociadas con Sun (el proyecto Pjama), pero no hay nada activo en este momento. Los enfoques JDO / Hibernate son más favorecidos en estos días.
Debo señalar que la persistencia ortogonal no es realmente a prueba de desastres en el sentido amplio. Por ejemplo, no puede tratar con:
- restablecimiento de las conexiones, etc. con sistemas "externos" después de un reinicio,
- errores de aplicación que causan la corrupción de datos persistentes, o
- pérdida de datos debido a que algo ha hecho que el sistema se caiga entre los puntos de control.
Para aquellos, no creo que haya soluciones generales que sean prácticas.
Hay varios frameworks disponibles comercialmente, Veritas, Sun HA, IBM HACMP, etc., que monitorearán automáticamente los procesos y los iniciarán en otro servidor en caso de falla.
También hay hardware costoso como el rango Tandem Nonstop de HP que puede sobrevivir a fallas internas de hardware.
Sin embargo, los softwares están construidos por la gente y a la gente le encanta equivocarse Considere la historia de precaución del programa IEFBR14 enviado con IBMs MVS. Básicamente es un programa ficticio de NOP que permite que los bits declarativos de JCL ocurran sin ejecutar realmente un programa. Este es el código fuente original completo:
IEFBR14 START
BR 14 Return addr in R14 -- branch at it
END
¿Nada de código será más sencillo? Durante su larga vida, este programa ha acumulado un informe de error y ahora está en la versión 4.
Eso es 1 error a tres líneas de código, la versión actual es cuatro veces el tamaño del original.
Los errores siempre aparecerán, solo asegúrate de que puedes recuperarte de ellos.
Intente utilizar un lenguaje interpretado de código abierto existente y vea si podría adaptar su implementación para incluir algunas de estas características. La implementación predeterminada de Python C incorpora un bloqueo interno (llamado GIL, Global Interpreter Lock) que se utiliza para "manejar" la concurrencia entre los hilos de Python tomando turnos cada ''n'' instrucciones de VM. Quizás podría conectarse a este mismo mecanismo para controlar el estado del código.
La aproximación más cercana parece ser SQL. Sin embargo, no es realmente un problema de idioma; Es sobre todo un problema de VM. Podría imaginar una máquina virtual Java con estas propiedades; Implementarlo sería otro asunto.
Una aproximación rápida y sucia se logra mediante el control de la aplicación. Pierdes la propiedad de "morir en cualquier momento", pero está bastante cerca.
La mayoría de estos esfuerzos, denominados " tolerancia a fallos ", se basan en el hardware, no en el software.
El ejemplo extremo de esto es Tandem , cuyas máquinas ''sin escalas'' tienen redundancia completa.
Implementar la tolerancia a fallos a nivel de hardware es atractivo porque una pila de software generalmente está hecha de componentes provenientes de diferentes proveedores; su aplicación de software de alta disponibilidad podría instalarse junto con otras aplicaciones y servicios decididamente inestables además de un sistema operativo que es inestable utilizando controladores de dispositivos de hardware que son decididamente frágiles.
Pero a nivel de idioma, casi todos los idiomas ofrecen las facilidades para una correcta verificación de errores. Sin embargo, incluso con RAII, excepciones, restricciones y transacciones, estas rutas de código rara vez se prueban correctamente y rara vez se prueban juntas en múltiples escenarios de fallas, y generalmente se encuentran en el código de control de errores que ocultan los errores. Por lo tanto, se trata más de la comprensión, la disciplina y las compensaciones del programador que de los lenguajes en sí.
Lo que nos lleva de nuevo a la tolerancia a fallos en el nivel de hardware. Si puede evitar que el enlace de su base de datos falle, puede evitar ejercer un código de manejo de errores poco fiable en las aplicaciones.
Para que un programa continúe donde lo dejó si la máquina pierde energía, no solo tendría que guardar el estado en algún lugar , el sistema operativo también tendría que "saber" para reanudarlo.
Supongo que la implementación de una función de "hibernación" en un lenguaje podría hacerse, pero tener eso sucediendo constantemente en segundo plano para que esté listo en caso de que algo malo suceda como el trabajo del sistema operativo, en mi opinión.
Por lo que sé, Ada se usa a menudo en sistemas críticos para la seguridad (a prueba de fallas).
Ada fue originalmente dirigido a sistemas integrados y en tiempo real.
Las características notables de Ada incluyen: mecanografía fuerte, mecanismos de modularidad (paquetes), verificación en tiempo de ejecución, procesamiento paralelo (tareas), manejo de excepciones y genéricos. Ada 95 agregó soporte para programación orientada a objetos, incluido el envío dinámico.
Ada admite verificaciones en tiempo de ejecución para protegerse contra el acceso a la memoria no asignada, errores de desbordamiento del búfer, errores off-by-one, errores de acceso a la matriz y otros errores detectables. Estas verificaciones pueden inhabilitarse en aras de la eficiencia del tiempo de ejecución, pero a menudo se pueden compilar de manera eficiente. También incluye facilidades para ayudar a la verificación del programa.
Por estas razones, Ada se usa ampliamente en sistemas críticos, donde cualquier anomalía puede tener consecuencias muy graves, es decir, muerte accidental o lesiones. Los ejemplos de sistemas en los que se usa Ada incluyen aviónica, sistemas de armas (incluidas las armas termonucleares) y naves espaciales.
La programación de N-Version también puede proporcionarle una lectura de fondo útil.
¹ Eso es básicamente un conocido que escribe software crítico de seguridad integrado
Si desea guardar la información del programa, ¿dónde la guardaría?
Tendría que ser guardado, por ejemplo, en el disco. Pero esto no lo ayudaría si el disco fallara, por lo que ya no está a prueba de desastres.
Solo obtendrás un cierto nivel de granularidad en tu estado guardado. Si desea algo como tihs, entonces probablemente el mejor enfoque es definir su nivel de granularidad, en términos de lo que constituye una operación atómica y guardar el estado en la base de datos antes de cada operación atómica. Luego, puede restaurar al punto de la operación atómica de ese nivel.
No conozco ningún idioma que haga esto automáticamente, ya que el costo de guardar el estado en el almacenamiento secundario es extremadamente alto. Por lo tanto, existe una compensación entre el nivel de granularidad y la eficiencia, que sería difícil de definir en una aplicación arbitraria.
Si el modo de falla se limita a una falla de hardware, VMware Fault Tolerance reclama algo similar a lo que desea. Ejecuta un par de máquinas virtuales a través de múltiples clústeres, y al usar lo que llaman vLockstep, vm primaria envía todos los estados a la vm secundaria en tiempo real, por lo que, en caso de falla primaria, la ejecución cambia de manera transparente a la secundaria.
Mi conjetura es que esto no ayudaría al fallo de comunicación, que es más común que el fallo de hardware. Para una alta disponibilidad seria, debe considerar sistemas distribuidos como el enfoque de grupo de procesos de Birman ( en papel en formato pdf , o reservar Sistemas distribuidos confiables: tecnologías, servicios web y aplicaciones ).
Si entiendo su pregunta correctamente, creo que está preguntando si es posible garantizar que un algoritmo en particular (es decir, un programa más cualquier opción de recuperación provista por el entorno) se complete (después de un número arbitrario de recuperaciones / reinicios).
Si esto es correcto, entonces le recomendaría el problema de la detención :
Dada una descripción de un programa y una entrada finita, decida si el programa termina de ejecutarse o se ejecutará para siempre, dada esa entrada.
Creo que clasificar su pregunta como una instancia del problema de detención es justo si se considera que lo ideal sería que el lenguaje fuera "a prueba de desastres", es decir, impartir una "perfección" a cualquier programa defectuoso o entorno caótico.
Esta clasificación reduce cualquier combinación de entorno, idioma y programa a "programa y una entrada finita".
Si estás de acuerdo conmigo, entonces te decepcionará leer que el problema de la detención es indecidible. Por lo tanto, no se puede demostrar que un lenguaje, compilador o entorno "a prueba de desastres" sea así.
Sin embargo, es completamente razonable diseñar un lenguaje que ofrezca opciones de recuperación para varios problemas comunes.
Si fuera a resolver su problema, escribiría un demonio (probablemente en C) que hizo toda la interacción de la base de datos en las transacciones para que no se inserten datos erróneos si se interrumpe. Luego haga que el sistema inicie este daemon al inicio.
Obviamente, desarrollar material web en C es bastante más lento que hacerlo en un lenguaje de scripting, pero funcionará mejor y será más estable (si escribes buen código, por supuesto :).
De manera realista, lo escribía en Ruby (o PHP o lo que sea) y hago que algo como el trabajo demorado (o el programador o el programador) lo ejecute de vez en cuando, porque no necesitaría actualizaciones del ciclo de reloj.
Espero que tenga sentido.
Windows Workflow Foundation puede resolver su problema. Está basado en .Net y está diseñado gráficamente como un flujo de trabajo con estados y acciones.
Permite la persistencia en la base de datos (ya sea automáticamente o cuando se le solicite). Podrías hacer esto entre estados / acciones. Esto serializa la instancia completa de su flujo de trabajo en la base de datos. Se rehidratará y la ejecución continuará cuando se cumpla una serie de condiciones (cierto tiempo, rehidratación programática, incendios de eventos, etc.)
Cuando se inicia un host WWF, comprueba la base de datos de persistencia y rehidrata los flujos de trabajo almacenados allí. Luego continúa ejecutándose desde el punto de persistencia.
Incluso si no desea utilizar los aspectos del flujo de trabajo, probablemente pueda seguir usando el servicio de persistencia.
Mientras sus pasos fueran atómicos, esto debería ser suficiente, especialmente porque supongo que tiene un UPS, por lo que podría monitorear los eventos de UPS y forzar la persistencia si se detecta un problema de alimentación.
La Memoria transaccional de software (STM) combinada con la RAM no volátil probablemente satisfaría la pregunta revisada del OP.
STM es una técnica para implementar "transacciones", por ejemplo, conjuntos de acciones que se realizan de manera efectiva como una operación atómica, o no se realizan en absoluto. Normalmente, el propósito de STM es permitir que los programas altamente paralelos interactúen sobre los recursos compartidos de una manera que sea más fácil de entender que la programación tradicional de bloqueo de recursos, y posiblemente tenga menos gastos generales en virtud de tener un estilo de bloqueo altamente optimista. programación.
La idea fundamental es simple: todas las lecturas y escrituras dentro de un bloque de "transacción" se registran (¡de alguna manera!); si cualquiera de los dos hilos entra en conflicto en estos conjuntos (conflictos de lectura-escritura o escritura-escritura) al final de cualquiera de sus transacciones, uno es elegido como ganador y continúa, y el otro se ve obligado a revertir su estado al principio De la transacción y volver a ejecutar.
Si uno insistía en que todos los cálculos eran transacciones y el estado al principio (/ final) de cada transacción se almacenaba en la RAM no volátil (NVRAM), un fallo de alimentación podría tratarse como un fallo de transacción que resultara en una "reversión". Los cálculos se procederían solo de los estados transaccionados de manera confiable. La NVRAM en estos días se puede implementar con memoria Flash o con batería de respaldo. Uno podría necesitar MUCHO NVRAM, ya que los programas tienen mucho estado (vea la historia de la minicomputadora al final). Alternativamente, los cambios de estado confirmados se podrían escribir en los archivos de registro que se escribieron en el disco; Este es el método estándar utilizado por la mayoría de las bases de datos y por sistemas de archivos confiables.
La pregunta actual con STM es: ¿qué tan costoso es hacer un seguimiento de los posibles conflictos de transacción? Si la implementación de STM ralentiza la máquina en una cantidad apreciable, las personas vivirán con esquemas existentes poco confiables en lugar de renunciar a ese rendimiento. Hasta ahora la historia no es buena, pero luego la investigación es temprana.
La gente generalmente no ha diseñado lenguajes para STM; para fines de investigación, en su mayoría han mejorado Java con STM (consulte el artículo de Comunicaciones de ACM en junio de este año). Escuché que MS tiene una versión experimental de C #. Intel tiene una versión experimental para C y C ++. La página de wikipedia tiene una larga lista. Y los chicos de programación funcional, como de costumbre, afirman que la propiedad libre de efectos secundarios de los programas funcionales hace que STM sea relativamente trivial de implementar en lenguajes funcionales.
Si recuerdo bien, en la década de los 70, hubo un considerable trabajo inicial en sistemas operativos distribuidos, en el que los procesos (código + estado) podían viajar de forma trivial de una máquina a otra. Creo que varios de estos sistemas permitieron explícitamente la falla del nodo, y podrían reiniciar un proceso en un nodo fallido desde el estado de guardado en otro nodo. El trabajo clave inicial fue en el sistema de computación distribuida por Dave Farber. Debido a que el diseño de lenguajes en la década de los 70 era popular, recuerdo que DCS tenía su propio lenguaje de programación pero no recuerdo el nombre. Si DCS no permitió el fallo de los nodos y se reinició, estoy bastante seguro de que lo hicieron los sistemas de investigación de seguimiento.
EDITAR: here se documenta un sistema de 1996 que a primera vista parece tener las propiedades que desea. Su concepto de transacciones atómicas es consistente con las ideas detrás de STM. (Va a demostrar que no hay muchas novedades bajo el sol).
Una nota al margen: en los años 70, Core Memory todavía era el rey. El núcleo, al ser magnético, no era volátil a través de fallas de alimentación, y muchas minicomputadoras (y estoy seguro de que los mainframes) tuvieron interrupciones de falla de alimentación que notificaron al software algunos milisegundos antes de la pérdida de energía. Usando eso, uno podría almacenar fácilmente el estado de registro de la máquina y apagarlo por completo. Cuando se restableció la energía, el control volvería a un punto de restauración del estado y el software podría continuar. Muchos programas podrían así sobrevivir a los parpadeos y reiniciarse de manera confiable. Personalmente construí un sistema de tiempo compartido en una minicomputadora Data General Nova; en realidad, podría tenerlo ejecutando 16 teletipos a toda velocidad, recibir un golpe de energía y volver a encender y reiniciar todos los teletipos como si nada hubiera pasado. El cambio de cacofonía a silencio y viceversa fue impresionante, lo sé, tuve que repetirlo muchas veces para depurar el código de administración de fallas de energía, y por supuesto fue una gran demostración (desconecte el enchufe, el silencio mortal, vuelva a conectarlo). .). El nombre del idioma que hizo esto fue, por supuesto, Ensamblador: -}
Erlang fue diseñado para su uso en sistemas de telecomunicaciones, donde la alta rel es fundamental. Creo que tienen una metodología estándar para crear conjuntos de procesos de comunicación en los que los fallos se pueden manejar con gracia.
ERLANG es un lenguaje funcional concurrente, adecuado para software distribuido, altamente concurrente y tolerante a fallas. Una parte importante de Erlang es su soporte para la recuperación de fallos. La tolerancia a fallos se proporciona al organizar los procesos de una aplicación ERLANG en estructuras de árbol. En estas estructuras, los procesos padres monitorean las fallas de sus hijos y son responsables de su reinicio.
"Así que un lenguaje que recordaría su estado en un momento dado, no importa si se corta la alimentación, y continúa donde se detuvo".
"continúa donde lo dejó" a menudo no es la estrategia de recuperación correcta. Ningún idioma o entorno en el mundo intentará adivinar cómo recuperarse de un fallo en particular automáticamente. Lo mejor que puede hacer es proporcionarle herramientas para escribir su propia estrategia de recuperación de una manera que no interfiera con su lógica empresarial, por ejemplo,
- Manejo de excepciones (para fallar rápido y aún garantizar la consistencia del estado)
- Transacciones (para revertir cambios incompletos)
- Flujos de trabajo (para definir rutinas de recuperación que se llaman automáticamente)
- Registro (para localizar la causa de un fallo)
- Inyección de AOP / dependencia (para evitar tener que insertar manualmente el código para hacer todo lo anterior)
Estas son herramientas muy genéricas y están disponibles en muchos idiomas y entornos.
No , no existe un lenguaje a prueba de desastres.
Editar:
A prueba de desastres implica la perfección. Trae a la mente imágenes de un proceso que aplica cierta inteligencia para resolver condiciones desconocidas, no especificadas e inesperadas de una manera lógica. No hay manera de que un lenguaje de programación pueda hacer esto. Si usted, como programador, no puede descubrir cómo va a fallar su programa y cómo recuperarse de él, entonces su programa tampoco podrá hacerlo.
El desastre desde una perspectiva de TI puede surgir de tantas maneras que ningún proceso puede resolver todos esos problemas diferentes. La idea de que podría diseñar un lenguaje para abordar todas las formas en que algo podría salir mal es simplemente incorrecta. Debido a la abstracción del hardware, muchos problemas no tienen mucho sentido de abordar con un lenguaje de programación; sin embargo, siguen siendo "desastres".
Por supuesto, una vez que comience a limitar el alcance del problema; Entonces podemos comenzar a hablar sobre el desarrollo de una solución para ello. Por lo tanto, cuando dejamos de hablar de ser a prueba de desastres y comenzamos a hablar de cómo recuperarse de aumentos repentinos de energía, es mucho más fácil desarrollar un lenguaje de programación para abordar esa preocupación, incluso cuando, quizás, no tiene mucho sentido manejar ese problema un nivel tan alto de la pila. Sin embargo, aventuraré una predicción de que, una vez que lo aplique a implementaciones realistas, dejará de ser interesante como lenguaje, ya que se ha vuelto tan específico. es decir, usar mi lenguaje de scripting para ejecutar procesos por lotes durante la noche que se recuperarán de subidas de energía inesperadas y conexiones de red perdidas (con algo de ayuda humana); Este no es un caso de negocio convincente para mi mente.
Por favor, no me malinterpretes. Hay algunas sugerencias excelentes dentro de este hilo, pero en mi opinión, no se acercan a nada que se acerque remotamente a prueba de desastres.
Respuesta precisa:
Ada y SPARK fueron diseñados para una máxima tolerancia a fallos y para mover todos los errores posibles a tiempo de compilación en lugar de en tiempo de ejecución. Ada fue diseñada por el Departamento de Defensa de los EE. UU. Para sistemas militares y de aviación, que se ejecuta en dispositivos integrados en cosas tales como aviones. La chispa es su descendiente. Hay otro lenguaje utilizado en los primeros programas espaciales de EE. UU., HAL / S orientado a manejar el fallo de HARDWARE y la corrupción de la memoria debido a los rayos cósmicos.
Respuesta práctica:
Nunca he conocido a nadie que pueda codificar Ada / Spark. Para la mayoría de los usuarios, la mejor respuesta es las variantes de SQL en un DBMS con conmutación por error automática y agrupación de servidores. Los controles de integridad garantizan la seguridad. Algo así como T-SQL o PL / SQL tiene seguridad transaccional completa, es Turing-complete y es bastante tolerante a los problemas.
Razón por la que no hay una mejor respuesta:
Por razones de rendimiento, no puede proporcionar durabilidad para cada operación del programa. Si lo hiciera, el procesamiento disminuiría a la velocidad de su almacenamiento no volátil más rápido. En el mejor de los casos, su rendimiento se reducirá por mil o millones de veces, debido a que CUALQUIER COSA es más lenta que el caché de la CPU o la memoria RAM.
Sería el equivalente a pasar de una CPU Core 2 Duo a la antigua CPU 8086; a lo sumo, podría hacer un par de cientos de operaciones por segundo. Excepto, esto sería incluso más lento.
En los casos en que existen frecuentes ciclos de energía o fallas de hardware, se utiliza algo como un DBMS, que garantiza ACID para cada operación importante . O bien, usa hardware que tiene un almacenamiento rápido y no volátil (flash, por ejemplo); esto es aún mucho más lento, pero si el procesamiento es simple, está bien.
En el mejor de los casos, su idioma le brinda buenos controles de seguridad en tiempo de compilación para los errores y generará excepciones en lugar de fallar. El manejo de excepciones es una característica de la mitad de los idiomas en uso ahora.
- Primero, implementar una aplicación tolerante a fallas. Uno donde, si tiene 8 funciones y 5 modos de falla, ha realizado el análisis y la prueba para demostrar que las 40 combinaciones funcionan según lo previsto (y según lo deseado por el cliente específico: es probable que dos no estén de acuerdo).
- en segundo lugar, agregue un lenguaje de scripting sobre el conjunto compatible de características tolerantes a fallas. Debe estar lo más cerca posible de ser apátrida, por lo que es casi seguro que algo que no sea de Turing está completo.
- por último, descubra cómo manejar la restauración y reparación del estado del lenguaje de scripting adaptado a cada modo de falla.