fix(syslog): a device through Docker's address stays one source when that address changes (#6717)
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 1m36s
CI / integration (push) Successful in 1m45s
CI / publish (push) Successful in 1m24s

Docker's ingress address depends on the node Steward's task is on, and sources
were keyed by (address, sender). After a task move the same device arrived under
a new key and became a new "offered" row, so its lines were counted, not kept.

A named sender is now one row whatever address it last came from, enforced by a
partial unique index. Migration syslog_005 folds rows sharing a name into the
added one and moves their lines and patterns across.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-30 14:02:04 -04:00
co-authored by Claude Opus 5.5
parent 57b2aafd1a
commit 50b8922895
5 changed files with 150 additions and 16 deletions
+7 -2
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from datetime import datetime, timezone
from sqlalchemy import (JSON, BigInteger, Boolean, DateTime, ForeignKey, Index, SmallInteger, String, Text,
UniqueConstraint)
UniqueConstraint, text)
from sqlalchemy.orm import Mapped, mapped_column
from steward.core.ids import time_ordered_id
@@ -17,6 +17,8 @@ class SyslogSource(Base):
Through a port Swarm publishes on its ingress (the default), every device
arrives from one address of Docker's own; there a row is a device by the
name its lines carry, `sender` (#6096). Everywhere else `sender` is "".
That address changes when Steward's task changes node, so a name has one
row and `address` follows it.
A sender Steward hasn't been told about is a row too, `state="offered"`:
its lines are counted and its newest kept as a sample, so the page can
@@ -25,7 +27,10 @@ class SyslogSource(Base):
on; `"ignored"` stops the offer and keeps counting.
"""
__tablename__ = "syslog_sources"
__table_args__ = (UniqueConstraint("address", "sender", name="uq_syslog_sources_address_sender"),)
__table_args__ = (
UniqueConstraint("address", "sender", name="uq_syslog_sources_address_sender"),
Index("uq_syslog_sources_sender", "sender", unique=True, postgresql_where=text("sender <> ''")),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
address: Mapped[str] = mapped_column(String(64), nullable=False)