MQTT 입력 중지 또는 결함
MQTT 기반 데이터소스가 스트리밍 중이었는데, 무언가 깨집니다:
- 추론이 스스로 실행 중에서 유휴/장애 상태로 전환됨 — 아무도 Stop을 누르지 않았습니다.
- 예측값이
NaN으로 돌아옴, 또는 대시보드에 그럴듯한 값이 전혀 보이지 않음. - MQTT 출력 싱크가 쓰기 실패를 보고함, 사유가
transport: not connected와 같음. - 태그 매핑이 저장 시점에 거부됨,
tag_outside_subscription_*메시지와 함께. - Enable 또는 Test Connection이 아예 실패함 — 아직 아무것도 스트리밍되지 않은 상태에서.
MQTT는 푸시(push) 프로토콜입니다 — 추론은 어댑터가 메시지 도착 시 채우는 캐시에서 매 틱마다 샘플 하나를 가져옵니다(pull). 아래의 각 원인은 사실 다른 각도에서 본 같은 질문입니다: 캐시가 충분히 최근에 사용 가능한 값을 받았는가?
이 동일한 증상 집합을 다섯 가지 뚜렷한 장애 모드가 공유합니다. 백엔드 로그를 grep하여 구분하세요 — 아래 예시는 모두 오류 코드/사유만 보여주며, 페이로드 내용이나 자격 증명은 결코 보여주지 않습니다.
docker compose -f docker-compose.release.yml logs backend | grep -iE "MqttAdapter|ChannelReadException|channel .* read failed|tag stale|disconnected|not connected"-
정체된(stale) 토픽 (느리거나 죽은 퍼블리셔). 장애 전에 경고가 발생합니다:
MqttAdapter tag stale: tag=<topic> age=<age> staleAfter=<window>Channel <index> (<tag>) read failed — stopping inferenceInference fault: <reason>캐시 만료 윈도우는
clamp(50 / SamplingHz, 5s, 60s)입니다 — 기본값인 10 Hz에서는 5초입니다. 이 윈도우보다 느리게 발행되는 토픽은 만료되어ChannelReadException을 일으키며, 런타임은 이를 죽은 센서와 동일하게 취급합니다: 모델에 정지된(고정된) 값을 공급하는 대신 추론을 중지합니다. -
NaN/Infinity센티널. MQTT 페이로드 파서는 오직 유한한(finite) 숫자만 허용합니다 —NaN,Infinity,-Infinity, 그리고1e400처럼 오버플로하는 리터럴은 캐시에 도달하기 전에 모두 거부됩니다. 이런 방식으로 “측정값 없음” 센티널을 발행하는 장치는 캐시된 값을 결코 갱신하지 못하므로, 위의 원인 1과 정확히 동일한 방식으로 만료되어 실패합니다 — 동일한 로그 줄, 동일한ChannelReadException. 설계상 “NaN이 모델에 도달했다”는 별도의 장애 모드는 존재하지 않습니다. 그렇게 되면 이후 모든 다운스트림 예측을 조용히 오염시키게 될 것이기 때문입니다. -
클라이언트 ID 축출(eviction). 다음을 찾으세요:
MqttAdapter disconnected (SessionTakenOver); reconnecting또는 출력 측에서, 쓰기가 다음과 함께 실패합니다:
transport: not connectedMQTT 브로커는 두 번째 클라이언트가 동일한 클라이언트 ID로 연결하면 기존 연결을 축출합니다. 현재 빌드에서는 다음 경우에만 이런 일이 발생합니다: 동일한 역할의 데이터소스 두 개(입력 두 개, 또는 출력 두 개)가 하나의 명시적 Client ID를 공유하거나, 외부 MQTT 클라이언트가 데이터소스에 구성된 id를 재사용하는 경우입니다.
-
구독한 Base Topic 밖의 태그. 태그 매핑을 저장(또는 데이터소스 활성화)하면 즉시 다음과 함께 실패합니다:
tag_outside_subscription_<tagRef>입력 어댑터는 매핑된 태그(또는 구성된 Base Topic 와일드카드)에만 구독합니다 — 그 범위 밖의 토픽 경로는 조용히 결코 값을 생성하지 않는 대신, 저장 시점에 거부됩니다.
-
소켓 수준 연결 실패. 스트리밍이 시작되기도 전에 Test Connection 또는 Enable이 다음 중 하나와 함께 실패합니다:
connect_failed 도달 불가 / 거부됨 / DNS 실패 timeout 연결 시도가 완료되기 전에 취소됨
백엔드 로그는 경과 시간과 예외 유형으로 이를 뒷받침합니다:
MqttAdapter connect failed: broker=<broker> elapsedMs=<ms> errorType=<type>
-
정체된 토픽 / NaN 센티널 (원인 1–2). 퍼블리셔의 주기를 고치거나, 유한하지 않은 센티널을 발행하지 않도록 한 뒤 데이터소스를 다시 활성화하세요 — 장애는 런타임을 유휴 상태로 무너뜨리므로, 복구는 자동이 아닙니다:
- 매핑된 토픽의 실제 발행 간격을 확인하고, 데이터소스에 구성된 Sampling Hz의 만료 윈도우와 비교하세요.
- 토픽이 정당하게 “측정값 없음”을 보고한다면, 장치/게이트웨이가 그 기간 동안
NaN/Infinity를 보내는 대신 단순히 발행하지 않도록 전환하세요 — 둘 다 동일하게(조용한 토픽) 취급되므로, 센티널을 보내는 데는 아무런 이점이 없습니다. - 데이터소스를 다시 활성화한 뒤(모델을 다시 로드하고 어댑터를 재연결), Start를 누르세요.
-
클라이언트 ID 축출 (원인 3). 명시적 Client ID를 공유하는 동일한 역할의 데이터소스(입력 두 개, 또는 출력 두 개) 각각에 서로 다른 값을 부여하거나, 필드를 비워 MQTTnet이 각 쪽에 고유 id를 자동 생성하도록 하세요. 외부 클라이언트가 여러분의 데이터소스 id를 재사용하고 있다면, 둘 중 하나를 변경하세요. 이는 동일한 구성으로 만들어진 입력/출력 쌍에는 적용되지 않습니다 — v1.28.0의 역할 접미사(
-in/-out)가 이미 그 둘이 충돌하지 않도록 하므로 편집이 필요 없습니다. -
구독 밖의 태그 (원인 4). 태그의 토픽 경로를 포함하도록 데이터소스의 Base Topic을 넓히거나, 태그의 토픽 경로를 현재 Base Topic 안으로 들어오도록 수정하세요. 매핑을 다시 저장하세요.
-
소켓 실패 (원인 5). 데스크톱이 아니라 백엔드 컨테이너 내부에서 호스트, 포트, 네트워크 도달 가능성을 확인하세요.
localhost를 브로커의 컨테이너/서비스 이름으로 교체하거나(또는 컨테이너에서 Docker 호스트로는host.docker.internal), Test Connection을 재시도하세요.
- 새 MQTT 입력을 활성화하기 전에 매핑된 모든 토픽의 발행 간격을 만료 윈도우와 대조해서 확인하세요 — 토픽이 정당하게 느리게 발행한다면 Sampling Hz를 낮춰 허용 범위를 넓히세요(1 Hz → 50초).
- 동일한 역할의 데이터소스 두 개 사이에 하나의 명시적 Client ID를 재사용하지 마세요
(입력 두 개, 또는 출력 두 개), 그리고 데이터소스의 Client ID를 외부 MQTT 클라이언트에
재사용하지 마세요. 동일한 구성의 입력/출력 쌍은 이미 안전합니다 — v1.28.0이 각 쪽에
자동으로 접미사(
-in/-out)를 붙입니다. - Base Topic을 실제로 매핑하는 태그에 맞게 좁게 설정하고, 이미 그 범위 안에 있는 토픽 경로를 가진 태그만 매핑하세요.
- “데이터 없음”을
NaN/Infinity로 회선에 인코딩하지 마세요. 보고할 것이 없을 때 조용해지는 장치는 동일하게 동작하며, 센티널 값보다 추론하기 쉽습니다. - 워크스테이션이 아니라 박스의 관점에서 Test Connection을 실행하세요 — 데스크톱에서
localhost로 도달 가능한 브로커라도 백엔드 컨테이너에서는 전혀 도달하지 못할 수 있습니다.
관련 항목
섹션 제목: “관련 항목”- 알림 — 이런 장애를 UI에 표시하는 비자발적 중지 알림과 출력 쓰기 실패 알림.
- 입력 데이터소스 연결 — 만료 윈도우, 재연결 동작, 유한하지 않은 값 거부에 대한 전체 내용.
- 출력 데이터소스 — MQTT 게시 QoS, 클라이언트 ID 접미사, Test Connection 진단.
- 데이터소스 다운 또는 장애 — 이 페이지가 MQTT용으로 특화한 일반 Enable/장애 런북.