Skip to content

Commit c08a27e

Browse files
userFRMclaude
andauthored
fix(config): ship the canonical [historical]/[streaming] section names (#811)
The TOML deserializer binds sections named [historical], [streaming], [grpc] and [auth], but the shipped config.default.toml, the from_file rustdoc example, and the configuration article all used the internal [mdds]/[fpss] names (and a non-existent [retry] section). serde maps those to no field, so a user copying the sample had every host, port, tls, flush, and ring override silently discarded with no error, while [auth] still matched and loaded creds, masking the failure. Rename the sample, the rustdoc example, and the article to the canonical names, and document the streaming wait-strategy knobs the sample was missing. Four config_file tests asserted overrides that the [mdds]/[fpss] names never applied; they passed only because the suite does not run under the config-file feature in the default matrix. Rename their sections so they exercise the real binding, and add a guard test pinning the canonical section names in the shipped sample (the existing parse test only checked values equal to the production defaults, so it could not catch this). Co-authored-by: Claude <noreply@anthropic.com>
1 parent eea9815 commit c08a27e

3 files changed

Lines changed: 65 additions & 19 deletions

File tree

crates/thetadatadx/config.default.toml

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# ── Historical Data (gRPC) ───────────────────────────────────────────────────
77
# Historical data is served via gRPC over TLS (not raw TCP).
88

9-
[mdds]
9+
[historical]
1010
host = "mdds-01.thetadata.us"
1111
port = 443
1212
tls = true
@@ -21,7 +21,7 @@ max_message_size = 4194304
2121
# ── Real-Time Streaming (TLS/TCP) ────────────────────────────────────────────
2222
# The client tries each host in order until one connects.
2323

24-
[fpss]
24+
[streaming]
2525
# Production servers (New Jersey datacenter)
2626
hosts = [
2727
"nj-a.thetadata.us:20000",
@@ -51,6 +51,27 @@ reconnect_wait_rate_limited = 130000
5151
# Increase if stream events are being dropped (see `dropped_event_count`).
5252
ring_size = 131072
5353

54+
# Outgoing write flushing: "batched" (default, coalesces writes) or
55+
# "immediate" (flush every frame, lowest send latency, more syscalls).
56+
flush_mode = "batched"
57+
58+
# Consumer wait strategy when the ring is empty. Presets:
59+
# "low_latency" (default) — spin, brief yield, then a short park
60+
# "balanced" — shorter spin, low idle CPU
61+
# "efficient" — minimal spin, longest park, lowest idle CPU
62+
# "busy_spin" — pure spin, no park (pins a core while idle)
63+
wait_strategy = "low_latency"
64+
65+
# Fine-tune the active strategy. These override the preset's spin/yield/
66+
# park counts; the values below are the "low_latency" defaults.
67+
wait_spin_iters = 100
68+
wait_yield_iters = 10
69+
wait_park_us = 50
70+
71+
# CPU core to pin the streaming consumer thread to. -1 (default) leaves
72+
# it unpinned; set a core index to reduce scheduler jitter on a busy box.
73+
consumer_cpu = -1
74+
5475
# ── Credentials ──────────────────────────────────────────────────────────────
5576
# Path to credentials file (line 1 = email, line 2 = password).
5677
# Can also be overridden programmatically via Credentials::new().
@@ -61,15 +82,15 @@ creds_file = "creds.txt"
6182
# ── Alternate Server Regions ─────────────────────────────────────────────────
6283
# Uncomment to use staging or dev servers. These are NOT stable.
6384

64-
# [fpss]
85+
# [streaming]
6586
# hosts = [
6687
# "nj-a.thetadata.us:20100",
6788
# "test-server.thetadata.us:20100",
6889
# "test-server.thetadata.us:20101",
6990
# ]
7091

7192
# Dev servers (frequent reboots, replays historical data):
72-
# [fpss]
93+
# [streaming]
7394
# hosts = [
7495
# "nj-a.thetadata.us:20200",
7596
# "test-server.thetadata.us:20200",

crates/thetadatadx/src/config/mod.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -822,15 +822,15 @@ mod config_file {
822822
/// # Example file
823823
///
824824
/// ```toml
825-
/// [mdds]
825+
/// [historical]
826826
/// host = "mdds-01.thetadata.us"
827827
/// port = 443
828828
/// tls = true
829829
///
830-
/// [fpss]
830+
/// [streaming]
831831
/// hosts = ["nj-a.thetadata.us:20000", "nj-b.thetadata.us:20000"]
832832
/// reconnect_wait = 2000
833-
/// queue_depth = 1_000_000
833+
/// ring_size = 131072
834834
/// flush_mode = "batched" # or "immediate"
835835
///
836836
/// [grpc]
@@ -874,8 +874,9 @@ mod config_file {
874874
let wait_strategy =
875875
StreamingWaitStrategy::parse(&cf.streaming.wait_strategy).unwrap_or_default();
876876

877-
// If [grpc].max_message_size_mb is set, it overrides [mdds].max_message_size.
878-
// The grpc section value is in MB; the mdds section value is in bytes.
877+
// If [grpc].max_message_size_mb is set, it overrides
878+
// [historical].max_message_size. The grpc section value is in
879+
// MB; the historical section value is in bytes.
879880
let max_message_size = if cf.grpc.max_message_size_mb
880881
!= DirectConfig::production().historical.max_message_size / (1024 * 1024)
881882
{
@@ -1025,11 +1026,11 @@ mod tests {
10251026
#[test]
10261027
fn partial_toml_overrides_only_specified() {
10271028
let toml = r#"
1028-
[mdds]
1029+
[historical]
10291030
host = "custom.example.com"
10301031
port = 8443
10311032
1032-
[fpss]
1033+
[streaming]
10331034
ring_size = 65536
10341035
"#;
10351036
let config = DirectConfig::from_toml_str(toml).unwrap();
@@ -1043,7 +1044,7 @@ mod tests {
10431044
#[test]
10441045
fn streaming_hosts_as_array() {
10451046
let toml = r#"
1046-
[fpss]
1047+
[streaming]
10471048
hosts = ["host-a.example.com:20000", "host-b.example.com:20001"]
10481049
"#;
10491050
let config = DirectConfig::from_toml_str(toml).unwrap();
@@ -1061,7 +1062,7 @@ mod tests {
10611062
#[test]
10621063
fn streaming_hosts_as_csv_string() {
10631064
let toml = r#"
1064-
[fpss]
1065+
[streaming]
10651066
hosts = "host-a.example.com:20000,host-b.example.com:20001"
10661067
"#;
10671068
let config = DirectConfig::from_toml_str(toml).unwrap();
@@ -1072,7 +1073,7 @@ mod tests {
10721073
#[test]
10731074
fn flush_mode_immediate() {
10741075
let toml = r#"
1075-
[fpss]
1076+
[streaming]
10761077
flush_mode = "immediate"
10771078
"#;
10781079
let config = DirectConfig::from_toml_str(toml).unwrap();
@@ -1082,7 +1083,7 @@ mod tests {
10821083
#[test]
10831084
fn flush_mode_batched_by_default() {
10841085
let toml = r#"
1085-
[fpss]
1086+
[streaming]
10861087
flush_mode = "batched"
10871088
"#;
10881089
let config = DirectConfig::from_toml_str(toml).unwrap();
@@ -1163,7 +1164,7 @@ mod tests {
11631164
#[test]
11641165
fn unknown_keys_are_ignored() {
11651166
let toml = r#"
1166-
[mdds]
1167+
[historical]
11671168
host = "mdds-01.thetadata.us"
11681169
port = 443
11691170
unknown_key = "should be ignored"
@@ -1186,6 +1187,29 @@ mod tests {
11861187
assert_eq!(config.streaming.hosts.len(), 4);
11871188
}
11881189

1190+
#[test]
1191+
fn config_default_toml_uses_canonical_section_names() {
1192+
// The deserializer binds `[historical]` / `[streaming]`; the
1193+
// internal vendor names `[mdds]` / `[fpss]` deserialize to
1194+
// nothing, so a sample shipping them silently discards every
1195+
// override. Asserting host==443 above can't catch that (those
1196+
// equal the production defaults), so pin the section names in
1197+
// the shipped sample directly.
1198+
let default_toml = include_str!("../../config.default.toml");
1199+
assert!(
1200+
default_toml.contains("[historical]"),
1201+
"config.default.toml must use the canonical [historical] section"
1202+
);
1203+
assert!(
1204+
default_toml.contains("[streaming]"),
1205+
"config.default.toml must use the canonical [streaming] section"
1206+
);
1207+
assert!(
1208+
!default_toml.contains("[mdds]") && !default_toml.contains("[fpss]"),
1209+
"config.default.toml must not ship the dead [mdds]/[fpss] section names"
1210+
);
1211+
}
1212+
11891213
#[test]
11901214
fn invalid_toml_returns_error() {
11911215
let result = DirectConfig::from_toml_str("this is not valid toml [[[");

docs-site/docs/articles/configuration.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ Every field above is available on all four language surfaces under the naming co
4646
With the `config-file` feature, Rust loads the same fields from TOML — useful for operating the [server binary](/server/) or any deployment where configuration should live outside code:
4747

4848
```toml
49-
[retry]
50-
max_attempts = 5
49+
[historical]
50+
host = "mdds-01.thetadata.us"
51+
port = 443
5152

52-
[fpss]
53+
[streaming]
5354
flush_mode = "immediate"
5455
hosts = ["host-a.example.com:20000", "host-b.example.com:20000"]
5556
```

0 commit comments

Comments
 (0)