Pregunta

Al crear servicios de sistema que deben tener una alta confiabilidad, a menudo termino escribiendo muchos mecanismos "a prueba de fallos" en el caso de cosas como: comunicaciones que se han ido (por ejemplo, comunicación con el DB), ¿qué pasaría si la energía se pierde 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 se ocuparía de 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ó.

¿Esto ya existe? Si es así, ¿dónde puedo encontrarlo? Si no, ¿por qué no se puede realizar esto? Me parece muy útil para sistemas críticos.

p.s. 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: Dado 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 se corta la alimentación, 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:

Imagina un lenguaje (tan simple como PHP por ejemplo), donde puedes hacer cosas como hacer consultas de base de datos, actuar sobre él, realizar manipulaciones de archivos, realizar manipulaciones de carpetas, etc.

Su característica principal, sin embargo, 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. etc.

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 las dejó.

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.

Muy mal ... 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 caen y no vuelven a activarse), ... donde hay una pausa aquí, ... soluciona los problemas, ... ..y continuar donde lo 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 momento 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.

Buscaré crear mi propio marco (con estado persistente e instantáneas tal vez) para agregar las funciones que estoy buscando en .Net u otra máquina virtual.

Gracias a todos por el aporte y las grandes ideas.

¿Fue útil?

Solución

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 controló una aplicación en ejecución en el almacén persistente, el estado del hilo actual se incluiría en el punto de control. Si la aplicación se bloqueó y la reinició 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. Estos incluyen averiguar cómo admitir subprocesos múltiples en el contexto de la persistencia ortogonal, averiguar 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 " fuera de " sistemas después de un reinicio,
  • errores de aplicación que causan daños en los datos persistentes, o
  • pérdida de datos debido a que algo ha hecho caer el sistema entre puntos de control.

Para ellos, no creo que haya soluciones generales que sean prácticas.

Otros consejos

Software Transactional Memory (STM) combinado con 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 una sobrecarga menor en virtud de tener un estilo de bloqueo altamente optimista. programación.

La idea fundamental es simple: todas las lecturas y escrituras dentro de una " transacción " el bloque se registra (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 insistió en que todos los cálculos eran transacciones, y el estado al principio (/ final) de cada transacción se almacenó en la RAM no volátil (NVRAM), un fallo de alimentación podría tratarse como un fallo de transacción que resulta 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 mini computadora 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; con fines de investigación, en su mayoría han Java mejorado con STM (ver 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 es habitual, 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 correctamente, en la década de los 70 hubo un trabajo inicial considerable en sistemas operativos distribuidos, en los cuales 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 temprano 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ó la falla del nodo y se reinició, estoy bastante seguro de que el seguimiento en los sistemas de investigación sí lo hizo.

EDITAR: Un sistema de 1996 que a primera vista parece tener las propiedades que usted desea. documentado aquí . 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 aún era el rey. El núcleo, al ser magnético, no era volátil a través de fallas de energía, y muchas minicomputadoras (y estoy seguro de que los mainframes) tuvieron interrupciones de falla de energía que notificaron al software unos 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 restauró la energía, el control regresarí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: -}

Por lo que sé, Ada se usa a menudo en seguridad crítica (a prueba de fallos) sistemas.

  

Ada estaba originalmente orientada a   Sistemas embebidos y en tiempo real.

     

Las características notables de Ada incluyen:   Mecanografía fuerte, mecanismos de modularidad.   (paquetes), control en tiempo de ejecución,   procesamiento paralelo (tareas), excepción   Manipulación, y genéricos. Ada 95 agregado   Soporte para orientado a objetos.   Programación, incluida la dinámica.   despacho.

     

Ada soporta verificaciones en tiempo de ejecución en orden   para proteger contra el acceso a   memoria no asignada, desbordamiento de búfer   errores, errores off-by-one, array   Errores de acceso, y otros detectables.   loco. Estas verificaciones se pueden desactivar en   el interés de la eficiencia en tiempo de ejecución,   pero a menudo se puede compilar de manera eficiente.   También incluye facilidades para ayudar.   verificación del programa.

     

Para estos   razones, Ada es ampliamente utilizado en   Sistemas críticos, donde cualquier anomalía.   podría llevar a muy graves   consecuencias, es decir, muerte accidental   o lesión. Ejemplos de sistemas donde   Ada se utiliza incluye aviónica, arma.   sistemas (incluyendo termonuclear   armas), y naves espaciales.

Programación de la versión N también puede proporcionarle una lectura de fondo útil.

¹ Eso es básicamente un conocido que escribe software crítico de seguridad integrado

Dudo que las características de 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 para el idioma cómo recuperarse.

Digamos que tu 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 estaba tratando de 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.

Hay 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.

La mayoría de estos esfuerzos, denominados ' tolerancia a fallos ', están alrededor de hardware, no el software.

El ejemplo extremo de esto es Tandem , cuyas máquinas 'sin escalas' tienen una redundancia completa.

La implementación de la tolerancia a fallos a nivel de hardware es atractiva porque una pila de software normalmente 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 inusual y utiliza 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 varias situaciones de falla, y generalmente se encuentran en el código de control de errores que ocultan los errores. Así que se trata más de la comprensión, la disciplina y las compensaciones de los programadores que de los lenguajes en sí mismos.

Lo que nos devuelve 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.

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 su programa va a fallar 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 empezar a hablar sobre el desarrollo de una solución para ello. Entonces, cuando dejamos de hablar de ser a prueba de desastres y comenzamos a hablar de cómo recuperarse de aumentos repentinos de energía, se vuelve 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 en un nivel tan alto de la pila. Sin embargo, me aventuraré a hacer 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 tensión inesperadas y conexiones de red perdidas (con algo de ayuda humana); Este no es un caso de negocios 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.

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 por 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 la arquitectura de von Neumann también.

Ahora imagine un sistema creado a partir de dos sistemas discretos de este tipo: 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 se detiene, ¿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 piden al programador que se ocupe de ellos.

También podría hacer una pausa: a 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 en 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 en caliente listos para funcionar, múltiples rutas de red entre hosts, etc.). ¡gratis!

Respuesta precisa:

Ada y SPARK fueron diseñados para una máxima tolerancia a fallas y para mover todos los errores posibles a tiempo de compilación en lugar de 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 la operación del programa cada . 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 las cachés de CPU o RAM.

Sería el equivalente a pasar de una CPU Core 2 Duo a la antigua CPU 8086: a lo sumo, podrías hacer un par de cientos de operaciones por segundo. Excepto, esto sería incluso más lento.

En los casos en los que existen frecuentes ciclos de energía o fallas de hardware, utiliza algo como un DBMS, que garantiza ACID para cada operación importante . O bien, utiliza hardware que tiene un almacenamiento rápido y no volátil (flash, por ejemplo). Esto sigue siendo mucho más lento, pero si el procesamiento es sencillo, 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.

Hay varios marcos comerciales disponibles como Veritas, Sun's HA, IBMs HACMP, etc. que supervisará automáticamente los procesos y los iniciará en otro servidor en caso de fallo.

También hay un hardware costoso como el rango de HP Tandem Nonstop que puede sobrevivir a fallas internas del hardware.

Sin embargo, los softwares están construidos por personas y a las personas les 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.

This question forced me to post this text

(Its quoted from HGTTG from Douglas Adams:)


Click, hum.

The huge grey Grebulon reconnaissance ship moved silently through the black void. It was travelling at fabulous, breathtaking speed, yet appeared, against the glimmering background of a billion distant stars to be moving not at all. It was just one dark speck frozen against an infinite granularity of brilliant night.

On board the ship, everything was as it had been for millennia, deeply dark and Silent.

Click, hum.

At least, almost everything.

Click, click, hum.

Click, hum, click, hum, click, hum.

Click, click, click, click, click, hum.

Hmmm.

A low level supervising program woke up a slightly higher level supervising program deep in the ship's semi-somnolent cyberbrain and reported to it that whenever it went click all it got was a hum.

The higher level supervising program asked it what it was supposed to get, and the low level supervising program said that it couldn't remember exactly, but thought it was probably more of a sort of distant satisfied sigh, wasn't it? It didn't know what this hum was. Click, hum, click, hum. That was all it was getting.

The higher level supervising program considered this and didn't like it. It asked the low level supervising program what exactly it was supervising and the low level supervising program said it couldn't remember that either, just that it was something that was meant to go click, sigh every ten years or so, which usually happened without fail. It had tried to consult its error look-up table but couldn't find it, which was why it had alerted the higher level supervising program to the problem .

The higher level supervising program went to consult one of its own look-up tables to find out what the low level supervising program was meant to be supervising.

It couldn't find the look-up table .

Odd.

It looked again. All it got was an error message. It tried to look up the error message in its error message look-up table and couldn't find that either. It allowed a couple of nanoseconds to go by while it went through all this again. Then it woke up its sector function supervisor.

The sector function supervisor hit immediate problems. It called its supervising agent which hit problems too. Within a few millionths of a second virtual circuits that had lain dormant, some for years, some for centuries, were flaring into life throughout the ship. Something, somewhere, had gone terribly wrong, but none of the supervising programs could tell what it was. At every level, vital instructions were missing, and the instructions about what to do in the event of discovering that vital instructions were missing, were also missing.

Small modules of software — agents — surged through the logical pathways, grouping, consulting, re-grouping. They quickly established that the ship's memory, all the way back to its central mission module, was in tatters. No amount of interrogation could determine what it was that had happened. Even the central mission module itself seemed to be damaged.

This made the whole problem very simple to deal with. Replace the central mission module. There was another one, a backup, an exact duplicate of the original. It had to be physically replaced because, for safety reasons, there was no link whatsoever between the original and its backup. Once the central mission module was replaced it could itself supervise the reconstruction of the rest of the system in every detail, and all would be well.

Robots were instructed to bring the backup central mission module from the shielded strong room, where they guarded it, to the ship's logic chamber for installation.

This involved the lengthy exchange of emergency codes and protocols as the robots interrogated the agents as to the authenticity of the instructions. At last the robots were satisfied that all procedures were correct. They unpacked the backup central mission module from its storage housing, carried it out of the storage chamber, fell out of the ship and went spinning off into the void.

This provided the first major clue as to what it was that was wrong.

Further investigation quickly established what it was that had happened. A meteorite had knocked a large hole in the ship. The ship had not previously detected this because the meteorite had neatly knocked out that part of the ship's processing equipment which was supposed to detect if the ship had been hit by a meteorite.

The first thing to do was to try to seal up the hole. This turned out to be impossible, because the ship's sensors couldn't see that there was a hole, and the supervisors which should have said that the sensors weren't working properly weren't working properly and kept saying that the sensors were fine. The ship could only deduce the existence of the hole from the fact that the robots had clearly fallen out of it, taking its spare brain, which would have enabled it to see the hole, with them.

The ship tried to think intelligently about this, failed, and then blanked out completely for a bit. It didn't realise it had blanked out, of course, because it had blanked out. It was merely surprised to see the stars jump. After the third time the stars jumped the ship finally realised that it must be blanking out, and that it was time to take some serious decisions.

It relaxed.

Then it realised it hadn't actually taken the serious decisions yet and panicked. It blanked out again for a bit. When it awoke again it sealed all the bulkheads around where it knew the unseen hole must be.

It clearly hadn't got to its destination yet, it thought, fitfully, but since it no longer had the faintest idea where its destination was or how to reach it, there seemed to be little point in continuing. It consulted what tiny scraps of instructions it could reconstruct from the tatters of its central mission module.

"Your !!!!! !!!!! !!!!! year mission is to !!!!! !!!!! !!!!! !!!!!, !!!!! !!!!! !!!!! !!!!!, land !!!!! !!!!! !!!!! a safe distance !!!!! !!!!! ..... ..... ..... .... , land ..... ..... ..... monitor it. !!!!! !!!!! !!!!!..."

All of the rest was complete garbage.

Before it blanked out for good the ship would have to pass on those instructions, such as they were, to its more primitive subsidiary systems.

It must also revive all of its crew.

There was another problem. While the crew was in hibernation, the minds of all of its members, their memories, their identities and their understanding of what they had come to do, had all been transferred into the ship's central mission module for safe keeping. The crew would not have the faintest idea of who they were or what they were doing there. Oh well.

Just before it blanked out for the final time, the ship realised that its engines were beginning to give out too.

The ship and its revived and confused crew coasted on under the control of its subsidiary automatic systems, which simply looked to land wherever they could find to land and monitor whatever they could find to monitor.

Try taking an existing open source interpreted language and see if you could adapt its implementation to include some of these features. Python's default C implementation embeds an internal lock (called the GIL, Global Interpreter Lock) that is used to "handle" concurrency among Python threads by taking turns every 'n' VM instructions. Perhaps you could hook into this same mechanism to checkpoint the code state.

For a program to continue where it left off if the machine loses power, not only would it need to save state to somewhere, the OS would also have to "know" to resume it.

I suppose implementing a "hibernate" feature in a language could be done, but having that happen constantly in the background so it's ready in the event anything bad happens sounds like the OS' job, in my opinion.

It's main feature however should be: If the power dies, and the thing restarts it takes of where it left off (So it not only remembers where it was, it will remember the variable states as well). Also, if it stopped in the middle of a filecopy, it will also properly resume. etc etc.

... ...

I've looked at erlang in the past. However nice it's fault tolerant features it has... It doesn't survive a powercut. When the code restarts you'll have to pick up the pieces

If such a technology existed, I'd be VERY interested in reading about it. That said, The Erlang solution would be having multiple nodes--ideally in different locations--so that if one location went down, the other nodes could pick up the slack. If all of your nodes were in the same location and on the same power source (not a very good idea for distributed systems), then you'd be out of luck as you mentioned in a comment follow-up.

The Microsoft Robotics Group has introduced a set of libraries that appear to be applicable to your question.

What is Concurrency and Coordination Runtime (CCR)?

Concurrency and Coordination Runtime (CCR) provides a highly concurrent programming model based on message-passing with powerful orchestration primitives enabling coordination of data and work without the use of manual threading, locks, semaphores, etc. CCR addresses the need of multi-core and concurrent applications by providing a programming model that facilitates managing asynchronous operations, dealing with concurrency, exploiting parallel hardware and handling partial failure.

What is Decentralized Software Services (DSS)?

Decentralized Software Services (DSS) provides a lightweight, state-oriented service model that combines representational state transfer (REST) with a formalized composition and event notification architecture enabling a system-level approach to building applications. In DSS, services are exposed as resources which are accessible both programmatically and for UI manipulation. By integrating service composition, structured state manipulation, and event notification with data isolation, DSS provides a uniform model for writing highly observable, loosely coupled applications running on a single node or across the network.

Most of the answers given are general purpose languages. You may want to look into more specialized languages that are used in embedded devices. The robot is a good example to think about. What would you want and/or expect a robot to do when it recovered from a power failure?

In the embedded world, this can be implemented through a watchdog interrupt and a battery-backed RAM. I've written such myself.

Depending upon your definition of a disaster, it can range from 'difficult' to 'practicly impossible' to delegate this responsibility to the language.

Other examples given include persisting the current state of the application to NVRAM after each statement is executed. This only works so long as the computer doesn't get destroyed.

How would a language level feature know to restart the application on a new host?

And in the situation of restoring the application to a host - what if significant time had passed and assumptions/checks made previously were now invalid?

T-SQL, PL/SQL and other transactional languages are probably as close as you'll get to 'disaster proof' - they either succeed (and the data is saved), or they don't. Excluding disabling transactional isolation, it's difficult (but probably not impossible if you really try hard) to get into 'unknown' states.

You can use techniques like SQL Mirroring to ensure that writes are saved in atleast two locations concurrently before a transaction is committed.

You still need to ensure you save your state every time it's safe (commit).

If I understand your question correctly, I think that you are asking whether it's possible to guarantee that a particular algorithm (that is, a program plus any recovery options provided by the environment) will complete (after any arbitrary number of recoveries/restarts).

If this is correct, then I would refer you to the halting problem:

Given a description of a program and a finite input, decide whether the program finishes running or will run forever, given that input.

I think that classifying your question as an instance of the halting problem is fair considering that you would ideally like the language to be "disaster proof" -- that is, imparting a "perfectness" to any flawed program or chaotic environment.

This classification reduces any combination of environment, language, and program down to "program and a finite input".

If you agree with me, then you'll be disappointed to read that the halting problem is undecidable. Therefore, no "disaster proof" language or compiler or environment could be proven to be so.

However, it is entirely reasonable to design a language that provides recovery options for various common problems.

In the case of power failure.. sounds like to me: "When your only tool is a hammer, every problem looks like a nail"

You don't solve power failure problems within a program. You solve this problem with backup power supplies, batteries, etc.

If the mode of failure is limited to hardware failure, VMware Fault Tolerance claims similar thing that you want. It runs a pair of virtual machines across multiple clusters, and using what they call vLockstep, the primary vm sends all states to the secondary vm real-time, so in case of primary failure, the execution transparently flips to the secondary.

My guess is that this wouldn't help communication failure, which is more common than hardware failure. For serious high availability, you should consider distributed systems like Birman's process group approach (paper in pdf format, or book Reliable Distributed Systems: Technologies, Web Services, and Applications ).

The closest approximation appears to be SQL. It's not really a language issue though; it's mostly a VM issue. I could imagine a Java VM with these properties; implementing it would be another matter.

A quick&dirty approximation is achieved by application checkpointing. You lose the "die at any moment" property, but it's pretty close.

I think its a fundemental mistake for recovery not to be a salient design issue. Punting responsibility exclusivly to the environment leads to a generally brittle solution intolerant of internal faults.

If it were me I would invest in reliable hardware AND design the software in a way that it was able to recover automatically from any possible condition. Per your example database session maintenance should be handled automatically by a sufficiently high level API. If you have to manually reconnect you are likely using the wrong API.

As others have pointed out procedure languages embedded in modern RDBMS systems are the best you are going to get without use of an exotic language.

VMs in general are designed for this sort of thing. You could use a VM vendors (vmware..et al) API to control periodic checkpointing within your application as appropriate.

VMWare in particular has a replay feature (Enhanced Execution Record) which records EVERYTHING and allows point in time playback. Obviously there is a massive performance hit with this approach but it would meet the requirements. I would just make sure your disk drives have a battery backed write cache.

You would most likely be able to find similiar solutions for java bytecode run inside a java virtual machine. Google fault tolerant JVM and virtual machine checkpointing.

If you do want the program information saved, where would you save it?

It would need to be saved e.g. to disk. But this wouldn't help you if the disk failed, so already it's not disaster-proof.

You are only going to get a certain level of granularity in your saved state. If you want something like tihs, then probably the best approach is to define your granularity level, in terms of what constitutes an atomic operation and save state to the database before each atomic operation. Then, you can restore to the point of that level atomic operation.

I don't know of any language that would do this automatically, sincethe cost of saving state to secondary storage is extremely high. Therefore, there is a tradeoff between level of granularity and efficiency, which would be hard to define in an arbitrary application.

  • First, implement a fault tolerant application. One where, where, if you have 8 features and 5 failure modes, you have done the analysis and test to demonstrate that all 40 combinations work as intended (and as desired by the specific customer: no two will likely agree).
  • second, add a scripting language on top of the supported set of fault-tolerant features. It needs to be as near to stateless as possible, so almost certainly something non-Turing-complete.
  • finally, work out how to handle restoration and repair of scripting language state adapted to each failure mode.

And yes, this is pretty much rocket science.

Windows Workflow Foundation may solve your problem. It's .Net based and is designed graphically as a workflow with states and actions.

It allows for persistence to the database (either automatically or when prompted). You could do this between states/actions. This Serialises the entire instance of your workflow into the database. It will be rehydrated and execution will continue when any of a number of conditions is met (certain time, rehydrated programatically, event fires, etc...)

When a WWF host starts, it checks the persistence DB and rehydrates any workflows stored there. It then continues to execute from the point of persistence.

Even if you don't want to use the workflow aspects, you can probably still just use the persistence service.

As long as your steps were atomic this should be sufficient - especially since I'm guessing you have a UPS so could monitor for UPS events and force persistence if a power issue is detected.

If I were going about solving your problem, I would write a daemon (probably in C) that did all database interaction in transactions so you won't get any bad data inserted if it gets interrupted. Then have the system start this daemon at startup.

Obviously developing web stuff in C is quite slower than doing it in a scripting language, but it will perform better and be more stable (if you write good code of course :).

Realistically, I'd write it in Ruby (or PHP or whatever) and have something like Delayed Job (or cron or whatever scheduler) run it every so often because I wouldn't need stuff updating ever clock cycle.

Hope that makes sense.

To my mind, the concept of failure recover is, most of the time, a business problem, not a hardware or language problem.

Take an example : you have one UI Tier and one subsystem. The subsystem is not very reliable but the client on the UI tier should percieve it as if it was.

Now, imagine that somehow your sub system crash, do you really think that the language you imagine, can think for you how to handle the UI Tier depending on this sub system ?

Your user should be explicitly aware that the subsystem is not reliable, if you use messaging to provide high reliability, the client MUST know that (if he isn't aware, the UI can just freeze waiting a response which can eventually come 2 weeks later). If he should be aware of this, this means that any abstrations to hide it will eventually leak.

By client, I mean end user. And the UI should reflect this unreliability and not hide it, a computer cannot think for you in that case.

"So a language which would remember it's state at any given moment, no matter if the power gets cut off, and continues where it left off."

"continues where it left off" is often not the correct recovery strategy. No language or environment in the world is going to attempt to guess how to recover from a particular fault automatically. The best it can do is provide you with tools to write your own recovery strategy in a way that doesn't interfere with your business logic, e.g.

  • Exception handling (to fail fast and still ensure consistency of state)
  • Transactions (to roll back incompleted changes)
  • Workflows (to define recovery routines that are called automatically)
  • Logging (for tracking down the cause of a fault)
  • AOP/dependency injection (to avoid having to manually insert code to do all the above)

These are very generic tools and are available in lots of languages and environments.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top