Skip to content

Commit 40e3585

Browse files
: config: introduce ConfigAttr and CONFIG meta attr
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 db2fa79 commit 40e3585

File tree

6 files changed

+240
-91
lines changed

6 files changed

+240
-91
lines changed

hyperactor/src/config.rs

Lines changed: 122 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,79 +34,169 @@ 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;
94+
95+
// /// This is a meta-attribute specifying the environment variable used by the configuration
96+
// /// key.
97+
// pub attr CONFIG_ENV_VAR: String;
98+
//
99+
// /// This is a meta-attribute specifying the name of the kwarg to pass to monarch.configure()
100+
// /// to set the attribute value in the global config.
101+
// pub attr PYTHON_CONFIG_KEY: String;
54102

55103
/// Maximum frame length for codec
56-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_CODEC_MAX_FRAME_LENGTH".to_string())
104+
@meta(CONFIG = ConfigAttr {
105+
env_name: Some("HYPERACTOR_CODEC_MAX_FRAME_LENGTH".to_string()),
106+
py_name: None,
107+
})
57108
pub attr CODEC_MAX_FRAME_LENGTH: usize = 10 * 1024 * 1024 * 1024; // 10 GiB
58109

59110
/// Message delivery timeout
60-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_DELIVERY_TIMEOUT".to_string())
111+
@meta(CONFIG = ConfigAttr {
112+
env_name: Some("HYPERACTOR_MESSAGE_DELIVERY_TIMEOUT".to_string()),
113+
py_name: None,
114+
})
61115
pub attr MESSAGE_DELIVERY_TIMEOUT: Duration = Duration::from_secs(30);
62116

63117
/// Timeout used by allocator for stopping a proc.
64-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_PROCESS_EXIT_TIMEOUT".to_string())
118+
@meta(CONFIG = ConfigAttr {
119+
env_name: Some("HYPERACTOR_PROCESS_EXIT_TIMEOUT".to_string()),
120+
py_name: None,
121+
})
65122
pub attr PROCESS_EXIT_TIMEOUT: Duration = Duration::from_secs(10);
66123

67124
/// Message acknowledgment interval
68-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_ACK_TIME_INTERVAL".to_string())
125+
@meta(CONFIG = ConfigAttr {
126+
env_name: Some("HYPERACTOR_MESSAGE_ACK_TIME_INTERVAL".to_string()),
127+
py_name: None,
128+
})
69129
pub attr MESSAGE_ACK_TIME_INTERVAL: Duration = Duration::from_millis(500);
70130

71131
/// Number of messages after which to send an acknowledgment
72-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_ACK_EVERY_N_MESSAGES".to_string())
132+
@meta(CONFIG = ConfigAttr {
133+
env_name: Some("HYPERACTOR_MESSAGE_ACK_EVERY_N_MESSAGES".to_string()),
134+
py_name: None,
135+
})
73136
pub attr MESSAGE_ACK_EVERY_N_MESSAGES: u64 = 1000;
74137

75138
/// Default hop Time-To-Live for message envelopes.
76-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESSAGE_TTL_DEFAULT".to_string())
139+
@meta(CONFIG = ConfigAttr {
140+
env_name: Some("HYPERACTOR_MESSAGE_TTL_DEFAULT".to_string()),
141+
py_name: None,
142+
})
77143
pub attr MESSAGE_TTL_DEFAULT : u8 = 64;
78144

79145
/// Maximum buffer size for split port messages
80-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_SPLIT_MAX_BUFFER_SIZE".to_string())
146+
@meta(CONFIG = ConfigAttr {
147+
env_name: Some("HYPERACTOR_SPLIT_MAX_BUFFER_SIZE".to_string()),
148+
py_name: None,
149+
})
81150
pub attr SPLIT_MAX_BUFFER_SIZE: usize = 5;
82151

83152
/// The maximum time an update can be buffered before being reduced.
84-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_SPLIT_MAX_BUFFER_AGE".to_string())
153+
@meta(CONFIG = ConfigAttr {
154+
env_name: Some("HYPERACTOR_SPLIT_MAX_BUFFER_AGE".to_string()),
155+
py_name: None,
156+
})
85157
pub attr SPLIT_MAX_BUFFER_AGE: Duration = Duration::from_millis(50);
86158

87159
/// Timeout used by proc mesh for stopping an actor.
88-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_STOP_ACTOR_TIMEOUT".to_string())
160+
@meta(CONFIG = ConfigAttr {
161+
env_name: Some("HYPERACTOR_STOP_ACTOR_TIMEOUT".to_string()),
162+
py_name: None,
163+
})
89164
pub attr STOP_ACTOR_TIMEOUT: Duration = Duration::from_secs(1);
90165

91166
/// Heartbeat interval for remote allocator
92-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_REMOTE_ALLOCATOR_HEARTBEAT_INTERVAL".to_string())
167+
@meta(CONFIG = ConfigAttr {
168+
env_name: Some("HYPERACTOR_REMOTE_ALLOCATOR_HEARTBEAT_INTERVAL".to_string()),
169+
py_name: None,
170+
})
93171
pub attr REMOTE_ALLOCATOR_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
94172

95173
/// The default encoding to be used.
96-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_DEFAULT_ENCODING".to_string())
174+
@meta(CONFIG = ConfigAttr {
175+
env_name: Some("HYPERACTOR_DEFAULT_ENCODING".to_string()),
176+
py_name: None,
177+
})
97178
pub attr DEFAULT_ENCODING: Encoding = Encoding::Multipart;
98179

99180
/// Whether to use multipart encoding for network channel communications.
100-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_CHANNEL_MULTIPART".to_string())
181+
@meta(CONFIG = ConfigAttr {
182+
env_name: Some("HYPERACTOR_CHANNEL_MULTIPART".to_string()),
183+
py_name: None,
184+
})
101185
pub attr CHANNEL_MULTIPART: bool = true;
102186

103187
/// 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())
188+
@meta(CONFIG = ConfigAttr {
189+
env_name: Some("HYPERACTOR_CHANNEL_NET_RX_BUFFER_FULL_CHECK_INTERVAL".to_string()),
190+
py_name: None,
191+
})
105192
pub attr CHANNEL_NET_RX_BUFFER_FULL_CHECK_INTERVAL: Duration = Duration::from_secs(5);
106193

107194
/// Sampling rate for logging message latency
108195
/// 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())
196+
@meta(CONFIG = ConfigAttr {
197+
env_name: Some("HYPERACTOR_MESSAGE_LATENCY_SAMPLING_RATE".to_string()),
198+
py_name: None,
199+
})
110200
pub attr MESSAGE_LATENCY_SAMPLING_RATE: f32 = 0.01;
111201

112202
/// Whether to enable client sequence assignment.
@@ -116,7 +206,10 @@ declare_attrs! {
116206
///
117207
/// Default: 10 seconds. If set to zero, disables the timeout and
118208
/// waits indefinitely.
119-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_HOST_SPAWN_READY_TIMEOUT".to_string())
209+
@meta(CONFIG = ConfigAttr {
210+
env_name: Some("HYPERACTOR_HOST_SPAWN_READY_TIMEOUT".to_string()),
211+
py_name: None,
212+
})
120213
pub attr HOST_SPAWN_READY_TIMEOUT: Duration = Duration::from_secs(10);
121214
}
122215

@@ -134,9 +227,14 @@ pub fn from_env() -> Attrs {
134227
}
135228

136229
for key in inventory::iter::<AttrKeyInfo>() {
137-
let Some(env_var) = key.meta.get(CONFIG_ENV_VAR) else {
230+
// New: look up CONFIG, then env_name inside it
231+
let Some(cfg_meta) = key.meta.get(CONFIG) else {
138232
continue;
139233
};
234+
let Some(env_var) = cfg_meta.env_name.as_deref() else {
235+
continue;
236+
};
237+
140238
let Ok(val) = env::var(env_var) else {
141239
// Default value
142240
output.push_str("# ");

0 commit comments

Comments
 (0)