El sistema operativo OpenVOS proporciona una interfaz de programación de aplicaciones (API) de alto nivel que, en general, facilita la programación del sistema. Pero a veces resulta demasiado fácil, ya que una simple llamada a una subrutina s$... puede ocultar una gran complejidad.
Esta es la primera de una serie irregular de publicaciones destinadas a llamar su atención sobre este tipo de dificultades para que pueda evitarlas en el diseño de sus aplicaciones.
Los males de los mensajes de la línea 25
La capacidad de escribir un mensaje en la línea25 (o la que sea la última) de un terminal permite a un usuario (o a un programa en ejecución) notificar a otros usuarios condiciones inusuales. La interfaz de línea de comandos es el comando send_message. El comando y los programas de usuario invocan en última instancia la subrutina API s$send_message. Los argumentos de esta llamada proporcionan el nombre de usuario del destinatario, el nombre del módulo de destino en el que ese usuario podría haber iniciado sesión, el texto de la notificación y algunos indicadores para modificar aspectos de la operación. Los llamantes con privilegios pueden especificar el nombre del remitente; de lo contrario, se utiliza por defecto el nombre de usuario del proceso de envío.
Hasta ahora parece sencillo. Dado que la operación requiere acceso al terminal propiedad de otro usuario, el núcleo envía la solicitud a una cola del servidor para el proceso TheOverseer en el módulo de destino. Normalmente, el proceso de envío espera el estado de entrega del mensaje, pero puede optar por no esperar esta respuesta.
El proceso Overseer de cualquier módulo realiza un seguimiento de todos los procesos de inicio de sesión y del dispositivo terminal utilizado por dichos procesos. Cuando recibe una solicitud para enviar un mensaje, busca en su lista de procesos y dispositivos, y para cada nombre de usuario del proceso que coincida con el argumento receiver_name, adjunta un puerto al dispositivo terminal del proceso, abre el puerto, escribe el mensaje en la línea de notificación, cierra el puerto y lo desvincula. Si varios procesos coinciden con el argumento receiver_name, este ciclo de adjuntar/abrir/escribir/cerrar/desvincular se repite para cada uno de ellos.
De acuerdo, esto es un poco más complicado de lo que habíamos hablado inicialmente. Al profundizar en el tema, descubrimos que cualquier módulo del sistema puede alojar procesos de inicio de sesión en los que el terminal real es un dispositivo de otro sistema/módulo que ha utilizado Open StrataLink (OSL) para iniciar sesión a través de la red (con el comando login –module). Ahora, la operación aparentemente sencilla de conectar/abrir/escribir/cerrar/desconectar en ese terminal tiene que implicar procesos OSL y llamadas a procedimientos remotos de red al módulo remoto, y es mucho más costosa que el manejo en el módulo.
La magnificación de los efectos se produce cuando el nombre de usuario del destinatario se especifica como un nombre de estrella que puede coincidir con varios o todos los usuarios que han iniciado sesión. Y se magnifica aún más si el módulo de destino también se especifica como un nombre de estrella. El peor caso sería enviar el mensaje al usuario «*.*» (todos los usuarios, todos los grupos) en el módulo «*» (todos los módulos del sistema actual).
Implementación
s$expand_module_starname(target_module) => module_list;
foreach module_name in module_list
send_overseer_request(module_name, send_message_request_data)
The message is sent via a server queue and OSL to TheOverseer process
on that module.
TheOverseer process on each receiving module then does:
foreach terminal_process in TheOverseer’s list of
login processes and sub-processes;
if (terminal_process user_name matches receiver star name)
if (messages_queued_for_device < 5)
queue it for the login device
else
reject the request (e$too_many_terminal_messages)
Simultáneamente, utilice hasta 10 puertos a la vez para enviar cualquier mensaje en cola
a los terminales;
Para cada mensaje de cada terminal
Conectar puerto
Abrir puerto
s$control(…WRITE_SYSTEM_MESSAGE…)
Cerrar puerto
Desconectar puerto
Este procesamiento se realiza en modo no_wait_mode para cada terminal, de modo que los retrasos (como las solicitudes de E/S a través de la red) solo afecten al procesamiento de mensajes de esa terminal.
Si algún mensaje no se puede entregar en 300 segundos, se elimina.
Cuando una aplicación utiliza este mecanismo para informar de errores y se produce una avalancha de errores, la aplicación puede verse fácilmente atrapada en la notificación, alternando entre esperar a que se procesen otros mensajes e inundar los terminales de los usuarios con mensajes que llegan demasiado rápido para ser comprendidos.
Evitación
Ahora que sabes lo que ocurre entre bastidores, el consejo que puedo darte es:
- Elija cuidadosamente los nombres de usuario y los nombres de los módulos para asegurarse de que el mensaje llegue solo a los usuarios que deben recibirlo y a nadie más. Por ejemplo, John_Doe.* o *.Operations o John_Doe.SysAdmin.
- Considera implementar una lista de usuarios sin nombre de usuario para notificar y enviar cada notificación por separado.
- Coloque los detalles de los mensajes de error en otro lugar (por ejemplo, en un registro de errores) y utilice s$send_message para alertar únicamente de la presencia de errores.
- Limita la frecuencia de las notificaciones a algo razonable (quizás no más de una vez por minuto).
- Suprime cualquier mensaje que sea igual al último enviado. El nuevo simplemente se superpondría al anterior.
Si tienes alguna otra sugerencia, publica tus comentarios para que los demás puedan verlos.
