Notification health events ========================== The notification health event stream is process-wide. It reports notification queue pressure, dropped notifications, recovery, incomplete notifications, malformed notification stream data, and consumer wait timeouts. Reading health events --------------------- .. code-block:: python import asyncio import pyNetX async def monitor(): while True: event = await pyNetX.next_notification_event_async(timeout_ms=-1) print(event.as_dict()) asyncio.run(monitor()) Synchronous helper: .. code-block:: python event = pyNetX.next_notification_event(timeout_ms=1000) print(event.as_dict()) Timeout behavior ---------------- ``timeout_ms=-1`` waits indefinitely. ``timeout_ms=0`` returns immediately if no event is available. Invalid negative values other than ``-1`` raise an error. If no event is available before the requested timeout, pyNetX returns a timeout event with: .. code-block:: python { "valid": False, "type": "timeout", "label": "None", "message": "No notification health event available before timeout", } Timeout events are generated by the global event bus, not by a specific ``NetconfClient``, so their label is ``"None"``. Event schema ------------ ``NotificationHealthEvent`` exposes readonly fields: .. list-table:: :header-rows: 1 * - Field - Type - Meaning * - ``valid`` - ``bool`` - ``False`` for timeout events, ``True`` for real health events. * - ``type`` - ``str`` - Event type such as ``notification_queue_full`` or ``malformed_notification``. * - ``timestamp`` - ``str`` - UTC ISO-8601 timestamp with millisecond precision, for example ``2026-06-25T05:35:15.123Z``. * - ``label`` - ``str`` - User-provided device label from ``NetconfClient(..., label="...")`` or ``"None"``. * - ``hostname`` - ``str`` - Device hostname/IP when available. * - ``port`` - ``int`` - Device NETCONF port when available. * - ``fd`` - ``int`` - Notification socket file descriptor when available. * - ``message`` - ``str`` - Human-readable message. * - ``queue_size`` - ``int`` - Queue size at event creation time. * - ``queue_max_size`` - ``int`` - Configured max queue size. ``-1`` means unbounded. * - ``queue_high_watermark`` - ``int`` - Highest queue depth observed by the client. * - ``notifications_enqueued`` - ``int`` - Total notifications successfully enqueued. * - ``notifications_dropped_queue_full`` - ``int`` - Total notifications dropped because the queue was full. * - ``notifications_dropped_delta`` - ``int`` - Drop delta represented by this event. * - ``incomplete_notifications_received`` - ``int`` - Number of incomplete notifications observed. * - ``partial_bytes`` - ``int`` - Partial or malformed notification bytes involved in a diagnostic event. * - ``health_events_dropped`` - ``int`` - Number of health events dropped by the bounded global event bus. ``event.as_dict()`` returns the same schema as a Python dictionary. Event types ----------- .. list-table:: :header-rows: 1 * - Event type - Meaning * - ``notification_queue_full`` - A bounded queue first became full and pyNetX dropped a notification. * - ``notification_drops_summary`` - Additional notifications were dropped while the queue remained full. * - ``notification_queue_recovered`` - A previously full queue has free capacity again. * - ``incomplete_notification`` - A partial notification did not receive the NETCONF EOM marker before a guard fired. * - ``timeout`` - No health event was available before the requested wait timeout. Production guidance ------------------- - Use ``label`` to map events to inventory names, not just IP addresses. - Run one or a small number of event monitor tasks per process. - Size ``set_threadpool_size(n)`` with long-lived ``next_notification_event_async(timeout_ms=-1)`` consumers in mind. - Monitor ``notifications_dropped_queue_full`` and ``health_events_dropped``. - Use bounded notification queues when memory limits are more important than lossless delivery.