Skip to content

Commit 28ec6d1

Browse files
committed
Shard editing command (#2732)
Allow editing the number of shard in a validator internal config file. Create a command `linera-server edit-shards --server file.json --num-shards 4 --host 'shard-%' --port 8000 --metrics-host 'shard-%' --metrics-port 8001` Because of port numbers, I figured we should use `%` when `num_shards` is written with 1 digits, `%%` for 2 digits, etc. ``` linera-server generate --validators configuration/local/validator_*.toml --committee committee.json --testing-prng-seed 1 linera-server edit-shards --server server_1.json --num-shards 02 --host 'shard-%%' --port '10000' --metrics-host 'shard-%%' --metrics-port '110%%' ```
1 parent 3537052 commit 28ec6d1

File tree

1 file changed

+151
-19
lines changed

1 file changed

+151
-19
lines changed

linera-service/src/server.rs

+151-19
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77
use std::{
88
borrow::Cow,
9-
num::NonZeroUsize,
9+
num::{NonZeroU16, NonZeroUsize},
1010
path::{Path, PathBuf},
1111
time::Duration,
1212
};
1313

14-
use anyhow::bail;
14+
use anyhow::{bail, Context};
1515
use async_trait::async_trait;
1616
use futures::{stream::FuturesUnordered, FutureExt as _, StreamExt, TryFutureExt as _};
1717
use linera_base::crypto::{CryptoRng, KeyPair};
@@ -395,6 +395,40 @@ enum ServerCommand {
395395
#[arg(long, default_value = "1000")]
396396
cache_size: usize,
397397
},
398+
399+
/// Replaces the configurations of the shards by following the given template.
400+
#[command(name = "edit-shards")]
401+
EditShards {
402+
/// Path to the file containing the server configuration of this Linera validator.
403+
#[arg(long = "server")]
404+
server_config_path: PathBuf,
405+
406+
/// The number N of shard configs to generate, possibly starting with zeroes. If
407+
/// `N` was written using `D` digits, we will replace the first occurrence of the
408+
/// string `"%" * D` (`%` repeated D times) by the shard number.
409+
#[arg(long)]
410+
num_shards: String,
411+
412+
/// The host of the validator (IP address or hostname), possibly containing `%`
413+
/// for digits of the shard number.
414+
#[arg(long)]
415+
host: String,
416+
417+
/// The port of the main endpoint, possibly containing `%` for digits of the shard
418+
/// number.
419+
#[arg(long)]
420+
port: String,
421+
422+
/// The host for the metrics endpoint, possibly containing `%` for digits of the
423+
/// shard number.
424+
#[arg(long)]
425+
metrics_host: String,
426+
427+
/// The port for the metrics endpoint, possibly containing `%` for digits of the
428+
/// shard number.
429+
#[arg(long)]
430+
metrics_port: Option<String>,
431+
},
398432
}
399433

400434
fn main() {
@@ -430,7 +464,7 @@ fn log_file_name_for(command: &ServerCommand) -> Cow<'static, str> {
430464
..
431465
} => {
432466
let server_config: ValidatorServerConfig =
433-
util::read_json(server_config_path).expect("Fail to read server config");
467+
util::read_json(server_config_path).expect("Failed to read server config");
434468
let name = &server_config.validator.name;
435469

436470
if let Some(shard) = shard {
@@ -440,13 +474,13 @@ fn log_file_name_for(command: &ServerCommand) -> Cow<'static, str> {
440474
}
441475
.into()
442476
}
443-
ServerCommand::Generate { .. } | ServerCommand::Initialize { .. } => "server".into(),
477+
ServerCommand::Generate { .. }
478+
| ServerCommand::Initialize { .. }
479+
| ServerCommand::EditShards { .. } => "server".into(),
444480
}
445481
}
446482

447483
async fn run(options: ServerOptions) {
448-
linera_version::VERSION_INFO.log();
449-
450484
match options.command {
451485
ServerCommand::Run {
452486
server_config_path,
@@ -461,10 +495,12 @@ async fn run(options: ServerOptions) {
461495
max_stream_queries,
462496
cache_size,
463497
} => {
498+
linera_version::VERSION_INFO.log();
499+
464500
let genesis_config: GenesisConfig =
465-
util::read_json(&genesis_config_path).expect("Fail to read initial chain config");
501+
util::read_json(&genesis_config_path).expect("Failed to read initial chain config");
466502
let server_config: ValidatorServerConfig =
467-
util::read_json(&server_config_path).expect("Fail to read server config");
503+
util::read_json(&server_config_path).expect("Failed to read server config");
468504

469505
#[cfg(feature = "rocksdb")]
470506
if server_config.internal_network.shards.len() > 1
@@ -521,17 +557,16 @@ async fn run(options: ServerOptions) {
521557
config_validators.push(Persist::into_value(server).validator);
522558
}
523559
if let Some(committee) = committee {
524-
Persist::persist(
525-
&mut persistent::File::new(
526-
&committee,
527-
CommitteeConfig {
528-
validators: config_validators,
529-
},
530-
)
531-
.expect("Unable to open committee configuration"),
560+
let mut config = persistent::File::new(
561+
&committee,
562+
CommitteeConfig {
563+
validators: config_validators,
564+
},
532565
)
533-
.await
534-
.expect("Unable to write committee description");
566+
.expect("Unable to open committee configuration");
567+
Persist::persist(&mut config)
568+
.await
569+
.expect("Unable to write committee description");
535570
info!("Wrote committee config {}", committee.to_str().unwrap());
536571
}
537572
}
@@ -544,7 +579,7 @@ async fn run(options: ServerOptions) {
544579
cache_size,
545580
} => {
546581
let genesis_config: GenesisConfig =
547-
util::read_json(&genesis_config_path).expect("Fail to read initial chain config");
582+
util::read_json(&genesis_config_path).expect("Failed to read initial chain config");
548583
let common_config = CommonStoreConfig {
549584
max_concurrent_queries,
550585
max_stream_queries,
@@ -558,9 +593,69 @@ async fn run(options: ServerOptions) {
558593
.await
559594
.unwrap();
560595
}
596+
597+
ServerCommand::EditShards {
598+
server_config_path,
599+
num_shards,
600+
host,
601+
port,
602+
metrics_host,
603+
metrics_port,
604+
} => {
605+
let mut server_config =
606+
persistent::File::<ValidatorServerConfig>::read(&server_config_path)
607+
.expect("Failed to read server config");
608+
let shards = generate_shard_configs(num_shards, host, port, metrics_host, metrics_port)
609+
.expect("Failed to generate shard configs");
610+
server_config.internal_network.shards = shards;
611+
Persist::persist(&mut server_config)
612+
.await
613+
.expect("Failed to write updated server config");
614+
}
561615
}
562616
}
563617

618+
fn generate_shard_configs(
619+
num_shards: String,
620+
host: String,
621+
port: String,
622+
metrics_host: String,
623+
metrics_port: Option<String>,
624+
) -> anyhow::Result<Vec<ShardConfig>> {
625+
let mut shards = Vec::new();
626+
let len = num_shards.len();
627+
let num_shards = num_shards
628+
.parse::<NonZeroU16>()
629+
.context("Failed to parse the number of shards")?;
630+
let pattern = "%".repeat(len);
631+
632+
for i in 1u16..=num_shards.into() {
633+
let index = format!("{i:0len$}");
634+
let host = host.replacen(&pattern, &index, 1);
635+
let port = port
636+
.replacen(&pattern, &index, 1)
637+
.parse()
638+
.context("Failed to decode port into an integers")?;
639+
let metrics_host = metrics_host.replacen(&pattern, &index, 1);
640+
let metrics_port = metrics_port
641+
.as_ref()
642+
.map(|port| {
643+
port.replacen(&pattern, &index, 1)
644+
.parse()
645+
.context("Failed to decode metrics port into an integers")
646+
})
647+
.transpose()?;
648+
let shard = ShardConfig {
649+
host,
650+
port,
651+
metrics_host,
652+
metrics_port,
653+
};
654+
shards.push(shard);
655+
}
656+
Ok(shards)
657+
}
658+
564659
#[cfg(test)]
565660
mod test {
566661
use linera_rpc::simple::TransportProtocol;
@@ -622,4 +717,41 @@ mod test {
622717
}
623718
);
624719
}
720+
721+
#[test]
722+
fn test_generate_shard_configs() {
723+
assert_eq!(
724+
generate_shard_configs(
725+
"02".into(),
726+
"host%%".into(),
727+
"10%%".into(),
728+
"metrics_host%%".into(),
729+
Some("11%%".into())
730+
)
731+
.unwrap(),
732+
vec![
733+
ShardConfig {
734+
host: "host01".into(),
735+
port: 1001,
736+
metrics_host: "metrics_host01".into(),
737+
metrics_port: Some(1101),
738+
},
739+
ShardConfig {
740+
host: "host02".into(),
741+
port: 1002,
742+
metrics_host: "metrics_host02".into(),
743+
metrics_port: Some(1102),
744+
},
745+
],
746+
);
747+
748+
assert!(generate_shard_configs(
749+
"2".into(),
750+
"host%%".into(),
751+
"10%%".into(),
752+
"metrics_host%%".into(),
753+
Some("11%%".into())
754+
)
755+
.is_err());
756+
}
625757
}

0 commit comments

Comments
 (0)