Skip to content

Commit ab42c12

Browse files
: config: introduce ConfigAttr and CONFIG meta attr (#1443)
Summary: this diff follows on from the discussions in D83778023 and lands `ConfigAttr` and the unified `CONFIG` meta-attribute for marking configuration keys, replaces all previous `CONFIG_ENV_VAR`/`PYTHON_CONFIG_KEY` usage, and narrows `attrs()` and `init_from_env()` to include only keys carrying this meta. it updates monarch python bindings to read `py_name` from `ConfigAttr`, rewrites affected documentation accordingly, and adds a unit test verifying that non-`CONFIG` keys are excluded from `attrs()`. all existing keys in hyperactor and hyperactor\_mesh have been migrated to the new form. Differential Revision: D83997813
1 parent 2898d76 commit ab42c12

File tree

6 files changed

+237
-92
lines changed

6 files changed

+237
-92
lines changed

hyperactor/src/config.rs

Lines changed: 117 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,79 +34,161 @@ use std::sync::LazyLock;
3434
use std::sync::RwLock;
3535
use std::time::Duration;
3636

37+
use serde::Deserialize;
38+
use serde::Serialize;
3739
use shell_quote::QuoteRefExt;
3840

41+
use crate as hyperactor;
3942
use crate::attrs::AttrKeyInfo;
43+
use crate::attrs::AttrValue;
4044
use crate::attrs::Attrs;
4145
use crate::attrs::SerializableValue;
4246
use crate::attrs::declare_attrs;
43-
use crate::data::Encoding;
47+
use crate::data::Encoding; // for macros
48+
49+
/// Metadata describing how a configuration key is exposed across
50+
/// environments.
51+
///
52+
/// Each `ConfigAttr` entry defines how a Rust configuration key maps
53+
/// to external representations:
54+
/// - `env_name`: the environment variable consulted by
55+
/// [`init_from_env()`] when loading configuration.
56+
/// - `py_name`: the Python keyword argument accepted by
57+
/// `monarch.configure(...)` and returned by `get_configuration()`.
58+
///
59+
/// All configuration keys should carry this meta-attribute via
60+
/// `@meta(CONFIG = ConfigAttr { ... })`.
61+
#[derive(Clone, Debug, Serialize, Deserialize, hyperactor::Named)]
62+
pub struct ConfigAttr {
63+
/// Environment variable consulted by `init_from_env()`.
64+
pub env_name: Option<String>,
65+
66+
/// Python kwarg name used by `monarch.configure(...)` and
67+
/// `get_configuration()`.
68+
pub py_name: Option<String>,
69+
}
70+
71+
impl AttrValue for ConfigAttr {
72+
fn display(&self) -> String {
73+
serde_json::to_string(self).unwrap_or_else(|_| "<invalid ConfigAttr>".into())
74+
}
75+
fn parse(s: &str) -> Result<Self, anyhow::Error> {
76+
Ok(serde_json::from_str(s)?)
77+
}
78+
}
4479

4580
// Declare configuration keys using the new attrs system with defaults
4681
declare_attrs! {
47-
/// This is a meta-attribute specifying the environment variable used by the configuration
48-
/// key.
49-
pub attr CONFIG_ENV_VAR: String;
50-
51-
/// This is a meta-attribute specifying the name of the kwarg to pass to monarch.configure()
52-
/// to set the attribute value in the global config.
53-
pub attr PYTHON_CONFIG_KEY: String;
82+
/// This is a meta-attribute marking a configuration key.
83+
///
84+
/// It carries metadata used to bridge Rust, environment
85+
/// variables, and Python:
86+
/// - `env_name`: environment variable name consulted by
87+
/// `init_from_env()`.
88+
/// - `py_name`: keyword argument name recognized by
89+
/// `monarch.configure(...)`.
90+
///
91+
/// All configuration keys should be annotated with this
92+
/// attribute.
93+
pub attr CONFIG: ConfigAttr;
5494

5595
/// Maximum frame length for codec
56-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_CODEC_MAX_FRAME_LENGTH".to_string())
96+
@meta(CONFIG = ConfigAttr {
97+
env_name: Some("HYPERACTOR_CODEC_MAX_FRAME_LENGTH".to_string()),
98+
py_name: None,
99+
})
57100
pub attr CODEC_MAX_FRAME_LENGTH: usize = 10 * 1024 * 1024 * 1024; // 10 GiB
58101

59102
/// Message delivery timeout
60-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_DELIVERY_TIMEOUT".to_string())
103+
@meta(CONFIG = ConfigAttr {
104+
env_name: Some("HYPERACTOR_MESSAGE_DELIVERY_TIMEOUT".to_string()),
105+
py_name: None,
106+
})
61107
pub attr MESSAGE_DELIVERY_TIMEOUT: Duration = Duration::from_secs(30);
62108

63109
/// Timeout used by allocator for stopping a proc.
64-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_PROCESS_EXIT_TIMEOUT".to_string())
110+
@meta(CONFIG = ConfigAttr {
111+
env_name: Some("HYPERACTOR_PROCESS_EXIT_TIMEOUT".to_string()),
112+
py_name: None,
113+
})
65114
pub attr PROCESS_EXIT_TIMEOUT: Duration = Duration::from_secs(10);
66115

67116
/// Message acknowledgment interval
68-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_ACK_TIME_INTERVAL".to_string())
117+
@meta(CONFIG = ConfigAttr {
118+
env_name: Some("HYPERACTOR_MESSAGE_ACK_TIME_INTERVAL".to_string()),
119+
py_name: None,
120+
})
69121
pub attr MESSAGE_ACK_TIME_INTERVAL: Duration = Duration::from_millis(500);
70122

71123
/// Number of messages after which to send an acknowledgment
72-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_ACK_EVERY_N_MESSAGES".to_string())
124+
@meta(CONFIG = ConfigAttr {
125+
env_name: Some("HYPERACTOR_MESSAGE_ACK_EVERY_N_MESSAGES".to_string()),
126+
py_name: None,
127+
})
73128
pub attr MESSAGE_ACK_EVERY_N_MESSAGES: u64 = 1000;
74129

75130
/// Default hop Time-To-Live for message envelopes.
76-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_TTL_DEFAULT".to_string())
131+
@meta(CONFIG = ConfigAttr {
132+
env_name: Some("HYPERACTOR_MESSAGE_TTL_DEFAULT".to_string()),
133+
py_name: None,
134+
})
77135
pub attr MESSAGE_TTL_DEFAULT : u8 = 64;
78136

79137
/// Maximum buffer size for split port messages
80-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_SPLIT_MAX_BUFFER_SIZE".to_string())
138+
@meta(CONFIG = ConfigAttr {
139+
env_name: Some("HYPERACTOR_SPLIT_MAX_BUFFER_SIZE".to_string()),
140+
py_name: None,
141+
})
81142
pub attr SPLIT_MAX_BUFFER_SIZE: usize = 5;
82143

83144
/// The maximum time an update can be buffered before being reduced.
84-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_SPLIT_MAX_BUFFER_AGE".to_string())
145+
@meta(CONFIG = ConfigAttr {
146+
env_name: Some("HYPERACTOR_SPLIT_MAX_BUFFER_AGE".to_string()),
147+
py_name: None,
148+
})
85149
pub attr SPLIT_MAX_BUFFER_AGE: Duration = Duration::from_millis(50);
86150

87151
/// Timeout used by proc mesh for stopping an actor.
88-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_STOP_ACTOR_TIMEOUT".to_string())
152+
@meta(CONFIG = ConfigAttr {
153+
env_name: Some("HYPERACTOR_STOP_ACTOR_TIMEOUT".to_string()),
154+
py_name: None,
155+
})
89156
pub attr STOP_ACTOR_TIMEOUT: Duration = Duration::from_secs(1);
90157

91158
/// Heartbeat interval for remote allocator
92-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_REMOTE_ALLOCATOR_HEARTBEAT_INTERVAL".to_string())
159+
@meta(CONFIG = ConfigAttr {
160+
env_name: Some("HYPERACTOR_REMOTE_ALLOCATOR_HEARTBEAT_INTERVAL".to_string()),
161+
py_name: None,
162+
})
93163
pub attr REMOTE_ALLOCATOR_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
94164

95165
/// The default encoding to be used.
96-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_DEFAULT_ENCODING".to_string())
166+
@meta(CONFIG = ConfigAttr {
167+
env_name: Some("HYPERACTOR_DEFAULT_ENCODING".to_string()),
168+
py_name: None,
169+
})
97170
pub attr DEFAULT_ENCODING: Encoding = Encoding::Multipart;
98171

99172
/// Whether to use multipart encoding for network channel communications.
100-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_CHANNEL_MULTIPART".to_string())
173+
@meta(CONFIG = ConfigAttr {
174+
env_name: Some("HYPERACTOR_CHANNEL_MULTIPART".to_string()),
175+
py_name: None,
176+
})
101177
pub attr CHANNEL_MULTIPART: bool = true;
102178

103179
/// How often to check for full MSPC channel on NetRx.
104-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_CHANNEL_NET_RX_BUFFER_FULL_CHECK_INTERVAL".to_string())
180+
@meta(CONFIG = ConfigAttr {
181+
env_name: Some("HYPERACTOR_CHANNEL_NET_RX_BUFFER_FULL_CHECK_INTERVAL".to_string()),
182+
py_name: None,
183+
})
105184
pub attr CHANNEL_NET_RX_BUFFER_FULL_CHECK_INTERVAL: Duration = Duration::from_secs(5);
106185

107186
/// Sampling rate for logging message latency
108187
/// Set to 0.01 for 1% sampling, 0.1 for 10% sampling, 0.90 for 90% sampling, etc.
109-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_LATENCY_SAMPLING_RATE".to_string())
188+
@meta(CONFIG = ConfigAttr {
189+
env_name: Some("HYPERACTOR_MESSAGE_LATENCY_SAMPLING_RATE".to_string()),
190+
py_name: None,
191+
})
110192
pub attr MESSAGE_LATENCY_SAMPLING_RATE: f32 = 0.01;
111193

112194
/// Whether to enable client sequence assignment.
@@ -116,7 +198,10 @@ declare_attrs! {
116198
///
117199
/// Default: 10 seconds. If set to zero, disables the timeout and
118200
/// waits indefinitely.
119-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_HOST_SPAWN_READY_TIMEOUT".to_string())
201+
@meta(CONFIG = ConfigAttr {
202+
env_name: Some("HYPERACTOR_HOST_SPAWN_READY_TIMEOUT".to_string()),
203+
py_name: None,
204+
})
120205
pub attr HOST_SPAWN_READY_TIMEOUT: Duration = Duration::from_secs(10);
121206
}
122207

@@ -134,9 +219,17 @@ pub fn from_env() -> Attrs {
134219
}
135220

136221
for key in inventory::iter::<AttrKeyInfo>() {
137-
let Some(env_var) = key.meta.get(CONFIG_ENV_VAR) else {
222+
// Skip keys that are not marked as CONFIG or that do not
223+
// declare an environment variable mapping. Only CONFIG-marked
224+
// keys with an `env_name` participate in environment
225+
// initialization.
226+
let Some(cfg_meta) = key.meta.get(CONFIG) else {
138227
continue;
139228
};
229+
let Some(env_var) = cfg_meta.env_name.as_deref() else {
230+
continue;
231+
};
232+
140233
let Ok(val) = env::var(env_var) else {
141234
// Default value
142235
output.push_str("# ");

0 commit comments

Comments
 (0)