Skip to content

Commit ba45427

Browse files
authored
Log connectivity code 1102 at info, 1101 at warn (#695) (#696)
Codes 1100-1102 are system messages, not data-farm notices, so they have no ConnectivityStatus and fall below WARNING_CODE_RANGE — all three landed in the error! branch of log_unrouted_notice. 1102 ("restored, data maintained") is IB's benign recovery message and tripped production alerting on every overnight reconnect. Grade system connectivity severity: 1102 -> info (benign), 1101 ("restored, data lost — resubscribe") -> warn, 1100 (lost) and everything else stay at error. Add named code constants and reuse them to build SYSTEM_MESSAGE_CODES. main-only: v2-stable has no log_unrouted_notice / ConnectivityStatus.
1 parent 6f52f56 commit ba45427

4 files changed

Lines changed: 47 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616

1717
### Fixed
1818

19+
- Connectivity restored notices no longer log at `error`: code 1102 ("data maintained") now logs at `info` and code 1101 ("data lost — resubscribe") at `warn`, so routine overnight reconnects stop tripping error-level alerting (#695).
1920
- Async snapshot market-data subscriptions no longer send a redundant cancel after the snapshot completes, matching the sync side (#686).
2021

2122
## [3.1.0] - 2026-06-19

src/messages.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,12 +1103,26 @@ pub const ORDER_CANCELLED_CODE: i32 = 202;
11031103
/// Range of error codes that are considered warnings (2100-2169).
11041104
pub const WARNING_CODE_RANGE: std::ops::RangeInclusive<i32> = 2100..=2169;
11051105

1106+
/// Connectivity between IB and TWS has been lost.
1107+
pub(crate) const CONNECTIVITY_LOST_CODE: i32 = 1100;
1108+
/// Connectivity restored, but market data was lost; resubscription is required.
1109+
pub(crate) const CONNECTIVITY_RESTORED_DATA_LOST_CODE: i32 = 1101;
1110+
/// Connectivity restored with market data maintained (nothing lost).
1111+
pub(crate) const CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE: i32 = 1102;
1112+
/// Socket port was reset during an active connection; the connection is dropped.
1113+
pub(crate) const SOCKET_PORT_RESET_CODE: i32 = 1300;
1114+
11061115
/// System message codes indicating connectivity status.
11071116
/// - 1100: Connectivity lost
11081117
/// - 1101: Connectivity restored, market data lost (resubscribe needed)
11091118
/// - 1102: Connectivity restored, market data maintained
11101119
/// - 1300: Socket port reset during active connection
1111-
pub const SYSTEM_MESSAGE_CODES: [i32; 4] = [1100, 1101, 1102, 1300];
1120+
pub const SYSTEM_MESSAGE_CODES: [i32; 4] = [
1121+
CONNECTIVITY_LOST_CODE,
1122+
CONNECTIVITY_RESTORED_DATA_LOST_CODE,
1123+
CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE,
1124+
SOCKET_PORT_RESET_CODE,
1125+
];
11121126

11131127
/// Data-advisory codes that TWS sends on a request which then proceeds
11141128
/// normally. The request is *not* rejected — the advisory announces a

src/transport/common.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,32 @@ use std::time::Duration;
44

55
use log::{error, info, warn};
66

7-
use crate::messages::{ConnectivityStatus, Notice};
7+
use crate::messages::{ConnectivityStatus, Notice, CONNECTIVITY_RESTORED_DATA_LOST_CODE, CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE};
88
use crate::subscriptions::common::RoutedItem;
99

1010
/// A notice reports *healthy* data-farm connectivity ("…connection is OK")
1111
/// rather than a problem. IB's message-codes reference classifies these as
1212
/// System Notifications, not warnings, so they're logged at info instead of
1313
/// warn. Only [`ConnectivityStatus::Ok`] is benign — `Broken`/`Inactive`/
1414
/// `Connecting` stay at warn via [`Notice::is_warning`].
15+
///
16+
/// Code 1102 ("connectivity restored — data maintained") is a system message,
17+
/// not a data-farm notice, so it has no [`ConnectivityStatus`]; it is treated
18+
/// as benign here because nothing was lost on the reconnect.
1519
fn is_benign_connectivity_notice(notice: &Notice) -> bool {
16-
notice.connectivity_status() == Some(ConnectivityStatus::Ok)
20+
notice.connectivity_status() == Some(ConnectivityStatus::Ok) || notice.code == CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE
1721
}
1822

1923
/// Log an unrouted notice (no subscription owner) at the appropriate severity.
24+
///
25+
/// System connectivity codes are graded by how much they matter: 1102
26+
/// (restored, data maintained) is benign → info; 1101 (restored, data lost —
27+
/// resubscribe required) is a warning; 1100 (connectivity lost) and everything
28+
/// else fall through to error.
2029
pub(crate) fn log_unrouted_notice(notice: &Notice) {
2130
if is_benign_connectivity_notice(notice) {
2231
info!("connectivity: {notice}");
23-
} else if notice.is_warning() {
32+
} else if notice.code == CONNECTIVITY_RESTORED_DATA_LOST_CODE || notice.is_warning() {
2433
warn!("warning: {notice}");
2534
} else {
2635
error!("error: {notice}");

src/transport/common_tests.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
11
use super::*;
2-
use crate::messages::FARM_OK_CODES;
2+
use crate::messages::{CONNECTIVITY_LOST_CODE, FARM_OK_CODES};
33

44
#[test]
55
fn test_is_benign_connectivity_notice() {
66
// Logging-policy invariant: only ConnectivityStatus::Ok (data-farm-OK
7-
// confirmations) is benign → info. Broken/Inactive/Connecting stay at warn.
7+
// confirmations) and system code 1102 (restored, data maintained) are
8+
// benign → info. Broken/Inactive/Connecting stay at warn.
89
for code in FARM_OK_CODES {
910
let notice = Notice::synthesized(code, "farm OK".into());
1011
assert!(is_benign_connectivity_notice(&notice), "code {code} should be benign");
1112
}
13+
// 1102: connectivity restored, market data maintained — nothing lost.
14+
let notice = Notice::synthesized(CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE, "restored, data maintained".into());
15+
assert!(is_benign_connectivity_notice(&notice), "code 1102 should be benign");
1216

1317
// Not benign: broken codes (Broken), inactive/connecting codes (still warn),
14-
// the range boundaries, and a code outside WARNING_CODE_RANGE entirely.
18+
// the range boundaries, a code outside WARNING_CODE_RANGE entirely, and the
19+
// non-benign system codes (1100 lost, 1101 restored-but-data-lost).
1520
for code in [
16-
2100, 2103, // Market data farm connection is broken
21+
2100,
22+
2103, // Market data farm connection is broken
1723
2105, // HMDS data farm connection is broken
1824
2157, // Sec-def data farm connection is broken
19-
2107, 2108, // inactive but available on demand — not benign
25+
2107,
26+
2108, // inactive but available on demand — not benign
2027
2119, // connecting — not benign
21-
2169, 200, // outside / boundary
28+
2169,
29+
200, // outside / boundary
30+
CONNECTIVITY_LOST_CODE, // 1100 — hard error
31+
CONNECTIVITY_RESTORED_DATA_LOST_CODE, // 1101 — warn (resubscribe)
2232
] {
2333
let notice = Notice::synthesized(code, "not benign".into());
2434
assert!(!is_benign_connectivity_notice(&notice), "code {code} should not be benign");
@@ -31,7 +41,10 @@ fn test_log_unrouted_notice_traverses_all_severities() {
3141
// emitted level. Drive each branch of log_unrouted_notice to confirm the
3242
// benign (info), warning (warn), and error paths are reachable and panic-free.
3343
log_unrouted_notice(&Notice::synthesized(FARM_OK_CODES[0], "farm OK".into()));
44+
log_unrouted_notice(&Notice::synthesized(CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE, "1102 info".into()));
3445
log_unrouted_notice(&Notice::synthesized(2103, "farm broken".into()));
46+
log_unrouted_notice(&Notice::synthesized(CONNECTIVITY_RESTORED_DATA_LOST_CODE, "1101 warn".into()));
47+
log_unrouted_notice(&Notice::synthesized(CONNECTIVITY_LOST_CODE, "1100 error".into()));
3548
log_unrouted_notice(&Notice::synthesized(200, "no security definition".into()));
3649
}
3750

0 commit comments

Comments
 (0)