Control de errores y gestión de un GenServer

Un popurrí de trucos y consejos para usar los GenServers, así como una explicación sobre cómo señalizar correctamente los errores en un GenServer mediante el átomo :stop. Síganme para más recetas: https://hexdocs.pm/elixir/GenServer.html 00:00 ¿Pero cuál de todas estas funciones debo usar? 03:35 Devolviendo :stop o :ignore en el init 07:51 Timeouts en el servidor 11:37 Cuando las cosas no salen bien: devolviendo :stop 15:10 Devolviendo :noreply en el handle_call. 17:50 Se acabó GenServer

Cuando trabajamos con GenServers en Elixir, es fundamental entender bien cuándo y cómo utilizar las distintas formas de comunicación que nos ofrece: call, cast y handle_info. Aunque a primera vista puede parecer que tenemos tres opciones para interactuar con nuestro GenServer, cada una tiene un propósito muy concreto y es importante no confundirlas.

En primer lugar, handle_info está pensado para gestionar mensajes del sistema, como notificaciones de monitores o enlaces que detectan caídas de procesos. No es recomendable usar handle_info para manejar respuestas normales ni tampoco deberíamos usar receive dentro de un GenServer, ya que perderíamos la abstracción que este nos proporciona y podríamos romper el flujo esperado. Por eso, handle_info queda reservado para mensajes que no provienen directamente de llamadas o envíos explícitos que hagamos.

Por otro lado, cast es una operación asíncrona que siempre responde con un :ok inmediato, sin importar si la operación interna fue exitosa o no. Esto significa que no tenemos forma directa de saber si la acción que hemos pedido se realizó correctamente, salvo que el proceso se caiga o que tengamos algún mecanismo externo de supervisión. Por esta razón, cast se usa en contadas ocasiones, cuando no necesitamos una confirmación o respuesta inmediata.

En cambio, call es la función predilecta para cuando necesitamos una respuesta. Es síncrona y bloquea al proceso llamante hasta que el GenServer responde o hasta que se agota un timeout. Esto nos permite saber si, por ejemplo, un elemento se añadió correctamente a una estructura interna o si hubo algún problema. Aunque a veces no parezca necesario, es habitual que queramos confirmar el resultado de una operación, y ahí call es la opción adecuada.

Además, es importante conocer que los callbacks de GenServer pueden devolver distintos tipos de respuestas más allá del clásico {:reply, respuesta, estado} o {:noreply, estado}. Por ejemplo, en la función init, que se ejecuta al arrancar el GenServer, podemos devolver {:stop, razón} para indicar que no se pudo iniciar correctamente, lo que hará que la función start o start_link falle y nos informe del motivo. Esto es útil si, por ejemplo, necesitamos establecer una conexión con una base de datos y esta no está disponible.

Otra opción es devolver :ignore en init, lo que indica que el GenServer no se iniciará, pero sin provocar una caída del sistema. Esto puede ser útil en árboles de supervisión donde el GenServer no es crítico y podemos continuar sin él.

También podemos establecer timeouts en init o en otros callbacks como handle_call y handle_cast. Esto nos permite definir un tiempo máximo para que el GenServer reciba un mensaje o complete una operación. Si se supera ese tiempo, se genera un mensaje :timeout que llega a handle_info, donde podemos manejarlo para depurar o tomar acciones específicas. Por ejemplo, si en init ponemos un Process.sleep que exceda el timeout, el GenServer no arrancará correctamente y podremos detectar ese fallo.

En cuanto a la gestión de errores dentro de los callbacks, podemos decidir terminar el GenServer si ocurre una situación grave. Por ejemplo, si implementamos una división y detectamos que el divisor es cero, podemos devolver un {:stop, razón, estado} para indicar que el proceso debe finalizar. Esto activa el callback terminate, donde podemos realizar tareas de limpieza o registro del motivo de la parada, ya que recibimos la razón y el estado final.

Un detalle interesante es que handle_call no está obligado a responder inmediatamente. Podemos devolver {:noreply, estado} para indicar que aún no tenemos la respuesta lista. Esto es útil en programación asíncrona, donde la respuesta puede tardar en computarse. En ese caso, somos responsables de llamar a GenServer.reply(pid, respuesta) cuando tengamos el resultado, y podemos usar funciones como Process.send_after para programar el envío de mensajes que disparen la respuesta más adelante. Así, mantenemos la interfaz síncrona para el llamante, pero gestionamos internamente la respuesta de forma asíncrona.

Por último, aunque no se profundizó en ello, existen otros callbacks útiles, como el que se ejecuta cuando recargamos el código del GenServer en caliente. Esto es especialmente valioso en despliegues en producción donde queremos actualizar la lógica sin detener el sistema, pudiendo ajustar el estado o realizar migraciones internas.

En definitiva, manejar GenServers con eficacia implica conocer bien estas sutilezas: cuándo usar cada tipo de llamada, cómo gestionar errores y estados especiales, y cómo aprovechar los timeouts y respuestas diferidas para controlar el comportamiento de nuestros procesos de forma robusta y elegante.

Lista de reproducción
  1. 1
    Cómo crear procesos
    11 minutos
  2. 2
    Cómo pasar mensajes entre procesos
    13 minutos
  3. 3
    Diccionario de un proceso y mantener un estado
    15 minutos
  4. 4
    Cómo enlazar procesos para detectar fallos
    13 minutos
  5. 5
    Cómo monitorizar procesos
    10 minutos
  6. 6
    ¿Qué es un GenServer?
    15 minutos
  7. 7
    Cómo enviar mensajes con un GenServer
    19 minutos
  8. 8
    Control de errores y gestión de un GenServer
    19 minutos
  9. 9
    Cómo renombrar procesos
    11 minutos
  10. 10
    Cómo crear un Supervisor Tree
    17 minutos
  11. 11
    Estrategias para trabajar con Supervisor
    18 minutos
  12. 12
    Estrategias para crear un Supervisor
    11 minutos
  13. 13
    Resumen sobre procesos OTP
    12 minutos
  14. 14
    Cómo usar Application
    12 minutos
  15. 15
    Ejemplo de Application con hijos
    14 minutos