feat: support env vars in heartbeat (#8064)

* feat: support reporting env vars in heartbeat messages to metasrv

Add `heartbeat_env_vars` config option for datanode and frontend. When
configured, the specified environment variable values are read at startup
and sent to metasrv in every heartbeat via the `extensions` map. Metasrv
extracts and stores them in `NodeInfo` for use in routing decisions
(e.g. AZ-aware region placement).

- Add `EnvVars` helper in `common/meta/src/datanode.rs` following the
  existing `GcStat` extension pattern with `into_extensions`/`from_extensions`
- Add `env_vars: HashMap<String, String>` field to `NodeInfo` in
  `common/meta/src/cluster.rs` with `#[serde(default)]` for backward compat
- Add `heartbeat_env_vars: Vec<String>` config field to `DatanodeOptions`,
  `FrontendOptions`, and `StandaloneOptions`
- Inject env vars into heartbeat `extensions` in both datanode and frontend
  heartbeat tasks (`datanode/src/heartbeat.rs`, `frontend/src/heartbeat.rs`)
- Extract env vars from `req.extensions` in all three metasrv
  `CollectXxxClusterInfoHandler`s
- Update `NodeInfo` construction sites in `meta-client`,
  `discovery/lease.rs`, and `standalone/information_extension.rs`
- Update expected TOML output in `tests-integration/tests/http.rs`
- Add unit tests for `EnvVars` round-trip and `NodeInfo` backward compat

Signed-off-by: Lei, HUANG <leih@nvidia.com>
Signed-off-by: Lei, HUANG <mrsatangel@gmail.com>

* refactor: address heartbeat env review feedback

Signed-off-by: Lei, HUANG <mrsatangel@gmail.com>

* chore: log error on deserialization failure

Signed-off-by: Lei, HUANG <mrsatangel@gmail.com>

* refactor: send heartbeat env vars once

Signed-off-by: Lei, HUANG <mrsatangel@gmail.com>

* fix: resend heartbeat env vars after reconnect

Signed-off-by: Lei, HUANG <mrsatangel@gmail.com>

* revert: keep env vars in every heartbeat

Signed-off-by: Lei, HUANG <mrsatangel@gmail.com>

---------

Signed-off-by: Lei, HUANG <leih@nvidia.com>
Signed-off-by: Lei, HUANG <mrsatangel@gmail.com>
This commit is contained in:
Lei, HUANG
2026-05-08 15:37:53 +08:00
committed by GitHub
parent d1873ca31d
commit 42aa58aa27
14 changed files with 227 additions and 13 deletions
+23 -1
View File
@@ -16,7 +16,7 @@ use std::time::Duration;
use cmd::options::GreptimeOptions;
use common_base::memory_limit::MemoryLimit;
use common_config::{Configurable, DEFAULT_DATA_HOME};
use common_config::{Configurable, DEFAULT_DATA_HOME, ENV_VAR_SEP};
use common_options::datanode::{ClientOptions, DatanodeClientOptions};
use common_telemetry::logging::{DEFAULT_LOGGING_DIR, DEFAULT_OTLP_HTTP_ENDPOINT, LoggingOptions};
use common_wal::config::DatanodeWalConfig;
@@ -311,3 +311,25 @@ fn test_load_standalone_example_config() {
};
similar_asserts::assert_eq!(options, expected);
}
#[test]
fn test_load_heartbeat_env_vars_from_env() {
let env_prefix = "HEARTBEAT_ENV_VARS_UT";
let env_key = [env_prefix, "HEARTBEAT_ENV_VARS"].join(ENV_VAR_SEP);
temp_env::with_var(env_key, Some("AZ,REGION"), || {
let expected = vec!["AZ".to_string(), "REGION".to_string()];
let datanode =
GreptimeOptions::<DatanodeOptions>::load_layered_options(None, env_prefix).unwrap();
similar_asserts::assert_eq!(datanode.component.heartbeat_env_vars, expected);
let frontend =
GreptimeOptions::<FrontendOptions>::load_layered_options(None, env_prefix).unwrap();
similar_asserts::assert_eq!(frontend.component.heartbeat_env_vars, expected);
let standalone =
GreptimeOptions::<StandaloneOptions>::load_layered_options(None, env_prefix).unwrap();
similar_asserts::assert_eq!(standalone.component.heartbeat_env_vars, expected);
});
}