improved logging
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import mmap
|
||||
from pathlib import Path
|
||||
import struct
|
||||
@@ -16,6 +17,8 @@ from python_app.orchestration.shm.decoder import (
|
||||
decode_trace_collection,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_HEADER_SIZE: Final[int] = 64
|
||||
_SLOT_HEADER_SIZE: Final[int] = 16
|
||||
_MAGIC: Final[bytes] = b"RDRRING2"
|
||||
@@ -43,6 +46,12 @@ class ShmRingReader:
|
||||
# A fail-fast open (absent/incompatible ring) must not leak the fd/mapping.
|
||||
self.close()
|
||||
raise
|
||||
logger.debug(
|
||||
"Opened SHM ring reader %s (capacity=%d, slot_size=%d bytes)",
|
||||
self._ring_name,
|
||||
self.capacity,
|
||||
self.slot_size_bytes,
|
||||
)
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close mmap and file handle."""
|
||||
@@ -50,6 +59,7 @@ class ShmRingReader:
|
||||
self._mmap.close()
|
||||
self._mmap = None
|
||||
self._file.close()
|
||||
logger.debug("Closed SHM ring reader %s", self._ring_name)
|
||||
|
||||
def pop_payload(self) -> bytes | None:
|
||||
"""Read next payload from ring, or `None` if no unread payload exists."""
|
||||
@@ -68,6 +78,7 @@ class ShmRingReader:
|
||||
sequence = self._read_u64(slot_offset + 8)
|
||||
if sequence != read_seq + 1:
|
||||
# Producer overwrote this slot before we read it. Resync to latest.
|
||||
logger.debug("Ring %s: slot lapped before read, resyncing to write_seq=%d", self._ring_name, write_seq)
|
||||
self._write_u64(32, write_seq)
|
||||
return None
|
||||
|
||||
@@ -75,6 +86,12 @@ class ShmRingReader:
|
||||
# Bound payload_size against the slot before slicing so a torn/garbage size
|
||||
# can never read out of the slot region; resync and skip on violation.
|
||||
if payload_size > self.slot_size_bytes:
|
||||
logger.debug(
|
||||
"Ring %s: payload_size %d exceeds slot %d, resyncing",
|
||||
self._ring_name,
|
||||
payload_size,
|
||||
self.slot_size_bytes,
|
||||
)
|
||||
self._write_u64(32, write_seq)
|
||||
return None
|
||||
|
||||
@@ -84,6 +101,7 @@ class ShmRingReader:
|
||||
# Re-read the slot sequence after the copy; if it changed, the producer
|
||||
# overwrote this slot mid-copy and the payload is torn — discard and resync.
|
||||
if self._read_u64(slot_offset + 8) != read_seq + 1:
|
||||
logger.debug("Ring %s: slot overwritten mid-copy, discarding torn payload", self._ring_name)
|
||||
self._write_u64(32, write_seq)
|
||||
return None
|
||||
|
||||
@@ -158,6 +176,7 @@ class ShmRingReader:
|
||||
return 0
|
||||
dropped = int(write_seq - read_seq)
|
||||
self._write_u64(32, write_seq)
|
||||
logger.debug("Ring %s: dropped %d unread payload(s)", self._ring_name, dropped)
|
||||
return dropped
|
||||
|
||||
@property
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import mmap
|
||||
import os
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from typing import Final
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_HEADER_SIZE: Final[int] = 64
|
||||
_SLOT_HEADER_SIZE: Final[int] = 16
|
||||
_MAGIC: Final[bytes] = b"RDRRING2"
|
||||
@@ -58,17 +61,26 @@ class ShmRingWriter:
|
||||
# Wrong-sized stale segment: drop it entirely and recreate, so the file
|
||||
# and any future mapping agree on geometry instead of being truncated
|
||||
# under a producer/consumer that still expects the old layout.
|
||||
if not created:
|
||||
logger.info("Ring %s: stale segment with wrong size, recreating", self._ring_name)
|
||||
self._file.truncate(self._mapped_size)
|
||||
created = True
|
||||
|
||||
self._mmap = mmap.mmap(self._file.fileno(), self._mapped_size)
|
||||
if created:
|
||||
self._initialize_header()
|
||||
logger.debug(
|
||||
"Created SHM ring writer %s (capacity=%d, slot_size=%d bytes)",
|
||||
self._ring_name,
|
||||
self._capacity,
|
||||
self._slot_size_bytes,
|
||||
)
|
||||
return
|
||||
|
||||
# Size matched but the header geometry/magic does not: the owner recreates
|
||||
# rather than diverge. Unlink and reopen as a brand-new ring.
|
||||
if not self._header_matches():
|
||||
logger.warning("Ring %s: header/geometry mismatch on existing segment, recreating", self._ring_name)
|
||||
self._mmap.close()
|
||||
self._file.close()
|
||||
self._unlink_if_present()
|
||||
@@ -77,6 +89,8 @@ class ShmRingWriter:
|
||||
self._file.truncate(self._mapped_size)
|
||||
self._mmap = mmap.mmap(self._file.fileno(), self._mapped_size)
|
||||
self._initialize_header()
|
||||
else:
|
||||
logger.debug("Reusing existing SHM ring writer %s", self._ring_name)
|
||||
|
||||
def _unlink_if_present(self) -> None:
|
||||
"""Remove the backing /dev/shm file if it exists (owner-only operation)."""
|
||||
@@ -89,6 +103,7 @@ class ShmRingWriter:
|
||||
"""Close mmap and file handle."""
|
||||
self._mmap.close()
|
||||
self._file.close()
|
||||
logger.debug("Closed SHM ring writer %s", self._ring_name)
|
||||
|
||||
def push(self, payload: bytes) -> bool:
|
||||
"""Push one payload with overwrite-oldest semantics on overflow."""
|
||||
@@ -98,6 +113,10 @@ class ShmRingWriter:
|
||||
write_seq = self._read_u64(24)
|
||||
read_seq = self._read_u64(32)
|
||||
if max(0, write_seq - read_seq) >= self._capacity:
|
||||
# Ring full: the consumer is not keeping up, so the oldest unread slot is
|
||||
# overwritten (overwrite-oldest). Logged at DEBUG to avoid flooding the
|
||||
# log when a backlog persists across many pushes.
|
||||
logger.debug("Ring %s: full, overwriting oldest unread slot", self._ring_name)
|
||||
# Advance the consumer cursor past the slot we are about to overwrite, but
|
||||
# re-read it first and move it only forward: a concurrent reader may have
|
||||
# already advanced it, and clobbering that backward would re-deliver an
|
||||
|
||||
Reference in New Issue
Block a user