From 8833534a090c46d26d3ee1d501ca3391cc332987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sun, 23 Mar 2025 17:36:08 +0100 Subject: [PATCH 01/43] feat(cli): send and receive message --- cli/fledger/src/main.rs | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index fd99ee76..cbfc25b5 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -1,13 +1,18 @@ +use anyhow::bail; use clap::{Parser, Subcommand}; use flarch::{ data_storage::{DataStorage, DataStorageFile}, + nodeids::U256, tasks::wait_ms, web_rtc::connection::{ConnectionConfig, HostLogin}, }; use flmodules::{ dht_router::broker::DHTRouter, dht_storage::broker::DHTStorage, + dht_storage::{broker::DHTStorage, core::RealmConfig, realm_view::RealmView}, + flo::{crypto::FloVerifier, realm::Realm}, + gossip_events::core::Event, network::{broker::NetworkIn, network_start, signal::SIGNAL_VERSION}, }; use flnode::{node::Node, version::VERSION_STRING}; @@ -74,6 +79,27 @@ pub struct Args { #[arg(long, default_value = "false")] log_dht_storage: bool, + /// Print new messages as they come + #[arg(long, default_value = "false")] + print_new_messages: bool, + + /// Timeout after which the node exists with nonzero code + /// If the timeout is 0, then it is disabled. + #[arg(long, default_value = "0")] + timeout: u32, + + /// Send a chat message upon node creation + /// If the message is an empty string, ignore it. + #[arg(long, default_value = "")] + send_chat_msg: String, + + /// Wait for a chat message with the given body. + /// Exit with 0 code if the message is received. + /// To be combined with the --timeout option. + /// If the message is an empty string, ignore it. + #[arg(long, default_value = "")] + recv_chat_msg: String, + #[command(subcommand)] command: Option, } @@ -101,6 +127,7 @@ struct Fledger { ds: DHTStorage, dr: DHTRouter, args: Args, + acked_msg_ids: Vec, } #[tokio::main] @@ -157,6 +184,7 @@ impl Fledger { dr: node.dht_router.as_ref().unwrap().clone(), node, args, + acked_msg_ids: Vec::new(), }; match f.args.command.clone() { @@ -182,6 +210,14 @@ impl Fledger { FledgerState::Duration(i) => log::info!("Just hanging around {i} seconds"), FledgerState::Forever => log::info!("Looping forever"), } + + // Handle --send-chat-msg + if self.args.send_chat_msg != "" { + self.node + .add_chat_message(self.args.send_chat_msg.clone()) + .await?; + } + loop { count += 1; @@ -209,6 +245,7 @@ impl Fledger { } { return Ok(()); } + self.ds.sync()?; println!( "dht-connections: {}/{}", @@ -222,6 +259,32 @@ impl Fledger { .collect::>() .join(", ") ); + + // Handle --recv-chat-msg + if self.args.recv_chat_msg != "" { + if self + .node + .gossip + .as_ref() + .unwrap() + .chat_events() + .iter() + .filter(|ev| ev.msg == self.args.recv_chat_msg) + .count() + > 0 + { + log::info!( + "Trigger message received: {}. Exiting.", + self.args.recv_chat_msg + ); + return Ok(()); + } + } + + // Handle --timeout + if timeout != 0 && timeout <= i { + bail!("Timeout reached."); + } } } @@ -267,6 +330,25 @@ impl Fledger { ); } } + + // Handle --print-new-messages + if self.args.print_new_messages { + let chat_events = self.node.gossip.as_ref().unwrap().chat_events(); + let chats: Vec<&Event> = chat_events + .iter() + .filter(|ev| !self.acked_msg_ids.contains(&ev.get_id())) + .collect(); + + if chats.len() <= 0 { + log::debug!("... No new message"); + } else { + log::info!("--- New Messages ---"); + for chat in chats { + self.acked_msg_ids.push(chat.get_id()); + log::info!(" [{}] {}", chat.src, chat.msg); + } + } + } } Ok(()) From 21cff7b44c93b57cc039de579de8c6a114d3f5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 24 Mar 2025 01:41:27 +0100 Subject: [PATCH 02/43] feat(experiment): exp1-dummy --- Makefile | 14 +++++ experiments/deploy-binaries.sh | 24 ++++++++ experiments/deploy-experiments.sh | 9 +++ experiments/exp1-dummy/explanation.png | Bin 0 -> 14017 bytes experiments/exp1-dummy/explanation.puml | 18 ++++++ experiments/exp1-dummy/fledger-recv.service | 12 ++++ experiments/exp1-dummy/fledger-send.service | 12 ++++ experiments/exp1-dummy/flsignal.service | 12 ++++ experiments/exp1-dummy/hosts | 3 + experiments/exp1-dummy/model.py | 14 +++++ experiments/exp1-dummy/playbook.yaml | 64 ++++++++++++++++++++ 11 files changed, 182 insertions(+) create mode 100755 experiments/deploy-binaries.sh create mode 100755 experiments/deploy-experiments.sh create mode 100644 experiments/exp1-dummy/explanation.png create mode 100644 experiments/exp1-dummy/explanation.puml create mode 100644 experiments/exp1-dummy/fledger-recv.service create mode 100644 experiments/exp1-dummy/fledger-send.service create mode 100644 experiments/exp1-dummy/flsignal.service create mode 100644 experiments/exp1-dummy/hosts create mode 100644 experiments/exp1-dummy/model.py create mode 100644 experiments/exp1-dummy/playbook.yaml diff --git a/Makefile b/Makefile index 5d2a1b90..f533ad78 100644 --- a/Makefile +++ b/Makefile @@ -85,12 +85,18 @@ kill: $(call PKILL,fledger) $(call PKILL,trunk serve) +devbox_check_or_abort: + @test "${DEVBOX_SHELL_ENABLED}" == "1" || (echo "Devbox not enabled. Aborting..."; exit 1) + build_cli: cd cli && cargo build -p fledger && cargo build -p flsignal build_cli_release: cd cli && cargo build --release -p fledger && cargo build --release -p flsignal +build_cli_release_musl: devbox_check_or_abort + cd cli && cargo build --release --target=x86_64-unknown-linux-musl -p fledger && cargo build --release --target=x86_64-unknown-linux-musl -p flsignal + build_web_release: cd flbrowser && trunk build --release @@ -123,6 +129,14 @@ docker_dev: docker push fledgre/$$cli:dev; \ done +deploy_experiments_binaries: build_cli_release_musl + @echo "INFO: run make deploy_experiments to rsync the experiment files." + @cd experiments && ./deploy-binaries.sh + +deploy_experiments: + @echo "INFO: run make deploy_experiments_binaries to recompile and upload the binaries." + @cd experiments && ./deploy-experiments.sh + clean: for c in ${CARGOS}; do \ echo "Cleaning $$c"; \ diff --git a/experiments/deploy-binaries.sh b/experiments/deploy-binaries.sh new file mode 100755 index 00000000..c33beef6 --- /dev/null +++ b/experiments/deploy-binaries.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if test "$#" -ne 0; then + echo "usage: $0" + exit 1 +fi + +if ! test -f "../target-common/x86_64-unknown-linux-musl/release/flsignal"; then + echo "ERROR: you did not compile flsignal with musl. The experiment will not work." + echo "Aborting..." + exit 1 +fi + +if ! test -f "../target-common/x86_64-unknown-linux-musl/release/fledger"; then + echo "ERROR: you did not compile fledger with musl. The experiment will not work." + echo "Aborting..." + exit 1 +fi + +echo "Copying fledger and flsignal binaries..." +scp \ + ../target-common/x86_64-unknown-linux-musl/release/fledger \ + ../target-common/x86_64-unknown-linux-musl/release/flsignal \ + sphere-fledger: diff --git a/experiments/deploy-experiments.sh b/experiments/deploy-experiments.sh new file mode 100755 index 00000000..8dbc6f60 --- /dev/null +++ b/experiments/deploy-experiments.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if test "$#" -ne 0; then + echo "usage: $0" + exit 1 +fi + +echo "Rsyncing experiment files..." +rsync -ah --info=progress2 ./ sphere-fledger:~/experiments/ diff --git a/experiments/exp1-dummy/explanation.png b/experiments/exp1-dummy/explanation.png new file mode 100644 index 0000000000000000000000000000000000000000..a8aa1f86706ed1bf9fc995e3d49ecf2d48d2bcb5 GIT binary patch literal 14017 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 literal 0 HcmV?d00001 diff --git a/experiments/exp1-dummy/explanation.puml b/experiments/exp1-dummy/explanation.puml new file mode 100644 index 00000000..5867247a --- /dev/null +++ b/experiments/exp1-dummy/explanation.puml @@ -0,0 +1,18 @@ +@startuml +participant "Node A" as A +participant "Node B" as B +hnote over A: signaling server +hnote over A: fledger (send) +hnote over B: fledger (recv) +rnote over A, B + Setup with signaling server +endrnote +A -> B: chat message +rnote over A, B + Timeout 10s +endrnote +rnote over B + //exit code 0// + //iff message received// +endrnote +@enduml diff --git a/experiments/exp1-dummy/fledger-recv.service b/experiments/exp1-dummy/fledger-recv.service new file mode 100644 index 00000000..254affa5 --- /dev/null +++ b/experiments/exp1-dummy/fledger-recv.service @@ -0,0 +1,12 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/fledger --config /root/flnode -vvv -s ws://10.0.0.1:8765 --disable-turn-stun --print-new-messages --recv-chat-msg message --timeout 10 +RemainAfterExit=yes +User=root + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp1-dummy/fledger-send.service b/experiments/exp1-dummy/fledger-send.service new file mode 100644 index 00000000..69fbd87e --- /dev/null +++ b/experiments/exp1-dummy/fledger-send.service @@ -0,0 +1,12 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/fledger --config /root/flnode -vvv -s ws://10.0.0.1:8765 --disable-turn-stun --send-chat-msg message --timeout 10 +RemainAfterExit=yes +User=root + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp1-dummy/flsignal.service b/experiments/exp1-dummy/flsignal.service new file mode 100644 index 00000000..787b6bad --- /dev/null +++ b/experiments/exp1-dummy/flsignal.service @@ -0,0 +1,12 @@ +[Unit] +Description=Fledger signaling server +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/flsignal -vvv +RemainAfterExit=yes +User=root + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp1-dummy/hosts b/experiments/exp1-dummy/hosts new file mode 100644 index 00000000..706e0289 --- /dev/null +++ b/experiments/exp1-dummy/hosts @@ -0,0 +1,3 @@ +[all] +a +b diff --git a/experiments/exp1-dummy/model.py b/experiments/exp1-dummy/model.py new file mode 100644 index 00000000..47d5cc10 --- /dev/null +++ b/experiments/exp1-dummy/model.py @@ -0,0 +1,14 @@ +from mergexp import * + +net = Network('two') + +def makeNode(name: str): + return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) + +sna = [makeNode(name) for name in ['a', 'b']] + +link = net.connect(sna) +link[sna[0]].socket.addrs = ip4('10.0.0.1/24') +link[sna[1]].socket.addrs = ip4('10.0.0.2/24') + +experiment(net) diff --git a/experiments/exp1-dummy/playbook.yaml b/experiments/exp1-dummy/playbook.yaml new file mode 100644 index 00000000..0429da02 --- /dev/null +++ b/experiments/exp1-dummy/playbook.yaml @@ -0,0 +1,64 @@ +- name: Setup for all nodes + hosts: all + become: true + tasks: + - name: Copy executable to nodes + ansible.builtin.copy: + src: ~/musl/ + dest: ~/ + mode: preserve + + - name: Remove config folders + ansible.builtin.file: + path: /root/flnode + state: absent + +- name: Setup node a + hosts: a + become: true + environment: + FLEDGER_SIGNAL_HOST: localhost + FLEDGER_MSG: message + tasks: + - name: Copy signaling service + ansible.builtin.copy: + src: flsignal.service + dest: /etc/systemd/system/ + mode: preserve + - name: Copy fledger service (send) + ansible.builtin.copy: + src: fledger-send.service + dest: /etc/systemd/system/ + mode: preserve + - name: Start signaling server + ansible.builtin.systemd_service: + name: flsignal + state: restarted + daemon_reload: true + no_block: true + - name: Start fledger node (send) + ansible.builtin.systemd_service: + name: fledger-send + state: restarted + no_block: true + +- name: Setup node b + hosts: b + become: true + environment: + FLEDGER_SIGNAL_HOST: 10.0.0.11 + FLEDGER_MSG: message + tasks: + - name: Copy fledger service (recv) + ansible.builtin.copy: + src: fledger-recv.service + dest: /etc/systemd/system/ + mode: preserve + - name: Start fledger node (recv) + ansible.builtin.systemd_service: + name: fledger-recv + state: restarted + daemon_reload: true + - name: Check exit code of fledger node + changed_when: false + ansible.builtin.command: '/bin/bash -c "test $(/usr/bin/systemctl show -P ExecMainStatus fledger-recv) -eq 0"' From 3256f105aeeb16f55b66650e6cddc893ccb76875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 24 Mar 2025 01:42:20 +0100 Subject: [PATCH 03/43] feat(experiment): exp2-dummy10 --- experiments/README.md | 88 ++++++++++++++++++ experiments/exp1-dummy/playbook.yaml | 6 -- experiments/exp2-dummy10/explanation.png | Bin 0 -> 49801 bytes experiments/exp2-dummy10/explanation.puml | 42 +++++++++ experiments/exp2-dummy10/fledger-recv.service | 13 +++ experiments/exp2-dummy10/fledger-send.service | 13 +++ experiments/exp2-dummy10/flsignal.service | 12 +++ experiments/exp2-dummy10/generate-env.sh | 28 ++++++ experiments/exp2-dummy10/hosts | 11 +++ experiments/exp2-dummy10/model.py | 17 ++++ experiments/exp2-dummy10/playbook.yaml | 74 +++++++++++++++ 11 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 experiments/README.md create mode 100644 experiments/exp2-dummy10/explanation.png create mode 100644 experiments/exp2-dummy10/explanation.puml create mode 100644 experiments/exp2-dummy10/fledger-recv.service create mode 100644 experiments/exp2-dummy10/fledger-send.service create mode 100644 experiments/exp2-dummy10/flsignal.service create mode 100755 experiments/exp2-dummy10/generate-env.sh create mode 100644 experiments/exp2-dummy10/hosts create mode 100644 experiments/exp2-dummy10/model.py create mode 100644 experiments/exp2-dummy10/playbook.yaml diff --git a/experiments/README.md b/experiments/README.md new file mode 100644 index 00000000..18d619e3 --- /dev/null +++ b/experiments/README.md @@ -0,0 +1,88 @@ +# MergeTB experiments + +## Generalities + +Those experiments are meant to be run on sphere. + +Each is composed of + +- A mergeTB model (`model.py`) +- An ansible playbook (`playbook.yaml`) +- The ansible inventory (`hosts`) +- A plantUML sequence diagram of the experiment (`explanation.puml`, `explanation.png`) +- Oneshot systemd services to run the actual nodes + +The services can either be ran synchronously (ansible blocks) or asynchronously +(ansible starts the node and goes to the next task). +Check `exp1-dummy` for an example of both +(fledger-send is blocking and fledger-recv is non-blocking). + +## Running experiments + +### Setup mergeTB + +Read the documentation and do the hello world. + +Then, create one XDC to be used for all experiments. +Use this configuration (read the documentation before using it): + +```bash +Host sphere + Hostname jump.sphere-testbed.net + Port 2022 + User YOUR_USER + IdentityFile ~/.ssh/merge_key + ServerAliveInterval 30 + +Host sphere-fledger + ProxyJump sphere + IdentityFile ~/.ssh/merge_key + User YOUR_USER + Hostname SSH_NAME_FROM_SPHERE +``` + +Don't forget to replace the values for `YOUR_USER` and `SSH_NAME_FROM_SPHERE`. + +On the XDC, install ansible (use the ansible/ansible apt ppa!). + +Configure ansible with this `~/.ansible.cfg` from the documentation : + +```bash +[defaults] +# don't check experiment node keys, if this is not set, you will have to +# explicitly accept the SSH key for each experiment node you run Ansible +# against +host_key_checking = False + +# configure up to 5 hosts in parallel +forks = 5 + +# tmp directory on the local non-shared filesystem. Useful when running ansible +# on multiple separate XDCs +local_tmp = /tmp/ansible/tmp + +[ssh_connection] + +# connection optimization that increases speed significantly +pipelining = True + +# control socket directory on the local non-shared filesystem. Useful when +# running ansible on multiple separate XDCs +control_path_dir = /tmp/ansible/cp +``` + +### Activate the nodes and attach the XDC + +Use the model.py to create the reservation and the activation (materialization). +Attach your XDC to the materialization. + +### Deploy and run + +- Go to the root of the project on your local machine. +- Activate devbox with `devbox shell`. +- Run `make deploy_experiments_binaries` to +compile and upload the binaries to the XDC. +- Run `make deploy_experiments` to upload the experiment files as well. +- SSH into your XDC. +- Go to the experiment's folder. +- Run `ansible-playbook -i hosts playbook.yaml` diff --git a/experiments/exp1-dummy/playbook.yaml b/experiments/exp1-dummy/playbook.yaml index 0429da02..5a4bab4b 100644 --- a/experiments/exp1-dummy/playbook.yaml +++ b/experiments/exp1-dummy/playbook.yaml @@ -16,9 +16,6 @@ - name: Setup node a hosts: a become: true - environment: - FLEDGER_SIGNAL_HOST: localhost - FLEDGER_MSG: message tasks: - name: Copy signaling service ansible.builtin.copy: @@ -45,9 +42,6 @@ - name: Setup node b hosts: b become: true - environment: - FLEDGER_SIGNAL_HOST: 10.0.0.11 - FLEDGER_MSG: message tasks: - name: Copy fledger service (recv) ansible.builtin.copy: diff --git a/experiments/exp2-dummy10/explanation.png b/experiments/exp2-dummy10/explanation.png new file mode 100644 index 0000000000000000000000000000000000000000..1f2457c763ae14230f4680f01d0c6dab6ea3be25 GIT binary patch literal 49801 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 t` C: **broadcast** //"AtoC"// +B -> D: **broadcast** //"BtoD"// +C -> E: **broadcast** //"CtoE"// +D -> F: **broadcast** //"DtoF"// +E -> G: **broadcast** //"EtoG"// +F -> H: **broadcast** //"FtoH"// +G -> I: **broadcast** //"GtoI"// +H -> J: **broadcast** //"HtoJ"// +I -> A: **broadcast** //"ItoA"// +J -> B: **broadcast** //"JtoB"// + +rnote across + Timeout 10s +endrnote +rnote across + //exit code 0// + //iff corresponding message received// +endrnote +@enduml diff --git a/experiments/exp2-dummy10/fledger-recv.service b/experiments/exp2-dummy10/fledger-recv.service new file mode 100644 index 00000000..9aeeb54c --- /dev/null +++ b/experiments/exp2-dummy10/fledger-recv.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --print-new-messages --recv-chat-msg ${FLEDGER_RECV_MSG} --timeout 10 +RemainAfterExit=yes +User=root +EnvironmentFile=/root/env.systemd + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/fledger-send.service b/experiments/exp2-dummy10/fledger-send.service new file mode 100644 index 00000000..14f8f277 --- /dev/null +++ b/experiments/exp2-dummy10/fledger-send.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --send-chat-msg ${FLEDGER_SEND_MSG} --timeout 10 +RemainAfterExit=yes +User=root +EnvironmentFile=/root/env.systemd + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/flsignal.service b/experiments/exp2-dummy10/flsignal.service new file mode 100644 index 00000000..787b6bad --- /dev/null +++ b/experiments/exp2-dummy10/flsignal.service @@ -0,0 +1,12 @@ +[Unit] +Description=Fledger signaling server +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/flsignal -vvv +RemainAfterExit=yes +User=root + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/generate-env.sh b/experiments/exp2-dummy10/generate-env.sh new file mode 100755 index 00000000..cebf4a15 --- /dev/null +++ b/experiments/exp2-dummy10/generate-env.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +signalhost="10.0.0.1" + +mkdir -p env.systemd + +messages=() + +for i in $(seq 10); do + message="$(openssl rand -hex 16)" + messages+=("$message") +done + +for i in $(seq 0 9); do + j=$((("$i" + 2) % 10)) + n=$(("$i" + 97)) + nodename=$(echo $n | awk '{printf("%c",$1)}') + envfile="env.systemd/${nodename}.env" + send_msg="${messages[$j]}" + recv_msg="${messages[$i]}" + echo "FLEDGER_FLSIGNAL_HOST=${signalhost}" >"$envfile" + echo "FLEDGER_SEND_MSG=${send_msg}" >>"$envfile" + echo "FLEDGER_RECV_MSG=${recv_msg}" >>"$envfile" + + echo "[node $nodename]" + echo " <- ${recv_msg} [$i]" + echo " -> ${send_msg} [$j]" +done diff --git a/experiments/exp2-dummy10/hosts b/experiments/exp2-dummy10/hosts new file mode 100644 index 00000000..6f026d0f --- /dev/null +++ b/experiments/exp2-dummy10/hosts @@ -0,0 +1,11 @@ +[all] +a +b +c +d +e +f +g +h +i +j diff --git a/experiments/exp2-dummy10/model.py b/experiments/exp2-dummy10/model.py new file mode 100644 index 00000000..30d00620 --- /dev/null +++ b/experiments/exp2-dummy10/model.py @@ -0,0 +1,17 @@ +from mergexp import * + +net = Network('exp2') + +def makeNode(i: int): + name = chr(ord('a') + i) + return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) + +sna = [makeNode(i) for i in range(10)] + +link = net.connect(sna) + +for i in range(10): + suffix = str(i + 1) + link[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") + +experiment(net) diff --git a/experiments/exp2-dummy10/playbook.yaml b/experiments/exp2-dummy10/playbook.yaml new file mode 100644 index 00000000..62d686b8 --- /dev/null +++ b/experiments/exp2-dummy10/playbook.yaml @@ -0,0 +1,74 @@ +- name: Generate environments on ansible host + hosts: 127.0.0.1 + connection: local + tasks: + - name: Generate environemnts + changed_when: true + ansible.builtin.command: + cmd: ./generate-env.sh + +- name: Clean nodes and upload files (binaries and services) + hosts: all + become: true + tasks: + - name: Copy executable to nodes + ansible.builtin.copy: + src: ~/musl/ + dest: ~/ + mode: preserve + + - name: Copy fledger service (send) + ansible.builtin.copy: + src: fledger-send.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Copy fledger service (recv) + ansible.builtin.copy: + src: fledger-recv.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Remove config folders + ansible.builtin.file: + path: /root/flnode + state: absent + +- name: Run signaling server on node A + hosts: a + become: true + tasks: + - name: Copy signaling service + ansible.builtin.copy: + src: flsignal.service + dest: /etc/systemd/system/ + mode: preserve + - name: Start signaling server + ansible.builtin.systemd_service: + name: flsignal + state: restarted + daemon_reload: true + no_block: true + +- name: Upload env and run fleger send / recv on each node + hosts: all + become: true + tasks: + - name: Copy env file to nodes + ansible.builtin.copy: + src: "env.systemd/{{ inventory_hostname }}.env" + dest: ~/env.systemd + mode: preserve + - name: Start fledger node (send) + ansible.builtin.systemd_service: + name: fledger-send + state: restarted + no_block: true + - name: Start fledger node (recv) + ansible.builtin.systemd_service: + name: fledger-recv + state: restarted + daemon_reload: true + - name: Check exit code of fledger node + changed_when: false + ansible.builtin.command: '/bin/bash -c "test $(/usr/bin/systemctl show -P ExecMainStatus fledger-recv) -eq 0"' From 6bd0f44cfc40bb77f2f7d49061b1c1cb90d88f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 24 Mar 2025 04:13:33 +0100 Subject: [PATCH 04/43] feat(experiment): exp3-dummy10-router --- experiments/README.md | 9 +- experiments/exp1-dummy/README.md | 9 ++ experiments/exp2-dummy10/README.md | 9 ++ experiments/exp3-dummy10-router/README.md | 9 ++ .../exp3-dummy10-router/fledger-recv.service | 13 +++ .../exp3-dummy10-router/fledger-send.service | 13 +++ .../exp3-dummy10-router/flsignal.service | 12 +++ .../exp3-dummy10-router/generate-env.sh | 35 +++++++ experiments/exp3-dummy10-router/hosts | 14 +++ experiments/exp3-dummy10-router/model.py | 34 ++++++ experiments/exp3-dummy10-router/network.png | Bin 0 -> 12760 bytes experiments/exp3-dummy10-router/network.puml | 31 ++++++ experiments/exp3-dummy10-router/playbook.yaml | 98 ++++++++++++++++++ 13 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 experiments/exp1-dummy/README.md create mode 100644 experiments/exp2-dummy10/README.md create mode 100644 experiments/exp3-dummy10-router/README.md create mode 100644 experiments/exp3-dummy10-router/fledger-recv.service create mode 100644 experiments/exp3-dummy10-router/fledger-send.service create mode 100644 experiments/exp3-dummy10-router/flsignal.service create mode 100755 experiments/exp3-dummy10-router/generate-env.sh create mode 100644 experiments/exp3-dummy10-router/hosts create mode 100644 experiments/exp3-dummy10-router/model.py create mode 100644 experiments/exp3-dummy10-router/network.png create mode 100644 experiments/exp3-dummy10-router/network.puml create mode 100644 experiments/exp3-dummy10-router/playbook.yaml diff --git a/experiments/README.md b/experiments/README.md index 18d619e3..c19d4621 100644 --- a/experiments/README.md +++ b/experiments/README.md @@ -4,14 +4,19 @@ Those experiments are meant to be run on sphere. -Each is composed of +Each is composed of: +- A README (`README.md`) - A mergeTB model (`model.py`) - An ansible playbook (`playbook.yaml`) - The ansible inventory (`hosts`) -- A plantUML sequence diagram of the experiment (`explanation.puml`, `explanation.png`) - Oneshot systemd services to run the actual nodes +And sometimes: + +- A plantUML sequence diagram of the experiment (`explanation.puml`, `explanation.png`) +- A plantUML network diagram (`network.puml`, `network.png`) + The services can either be ran synchronously (ansible blocks) or asynchronously (ansible starts the node and goes to the next task). Check `exp1-dummy` for an example of both diff --git a/experiments/exp1-dummy/README.md b/experiments/exp1-dummy/README.md new file mode 100644 index 00000000..e9469a2b --- /dev/null +++ b/experiments/exp1-dummy/README.md @@ -0,0 +1,9 @@ +# Exp1 dummy + +Testing the waters. + +Run two nodes. On node A, send a message. On node B, wait for that message. + +If nodeB gets the message, the playbook succeeds. + +If the timeout of 10s is reached, the playbook fails. diff --git a/experiments/exp2-dummy10/README.md b/experiments/exp2-dummy10/README.md new file mode 100644 index 00000000..c1851ef1 --- /dev/null +++ b/experiments/exp2-dummy10/README.md @@ -0,0 +1,9 @@ +# Exp2 dummy10 + +Run 10 nodes, fully interconnected. + +Each node broadcasts a message (destined to a single node), +and waits for a message from one specific other node. + +The playbook succeeds if all nodes receive their message, +and fails if the timeout of 10s is reached. diff --git a/experiments/exp3-dummy10-router/README.md b/experiments/exp3-dummy10-router/README.md new file mode 100644 index 00000000..8f816005 --- /dev/null +++ b/experiments/exp3-dummy10-router/README.md @@ -0,0 +1,9 @@ +# Exp3 dummy10-router + +Same as exp2 but with two lans of 5 nodes separated by a router + +- Timeout for send is 10min, timeout for recv is 60sec. +- There is a separate signal server + +No nice plantuml diagram for the experiment here, +it's the same as exp2 but the topology changes. diff --git a/experiments/exp3-dummy10-router/fledger-recv.service b/experiments/exp3-dummy10-router/fledger-recv.service new file mode 100644 index 00000000..9acc2588 --- /dev/null +++ b/experiments/exp3-dummy10-router/fledger-recv.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --print-new-messages --recv-chat-msg ${FLEDGER_RECV_MSG} --timeout 60 +RemainAfterExit=yes +User=root +EnvironmentFile=/root/env.systemd + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/fledger-send.service b/experiments/exp3-dummy10-router/fledger-send.service new file mode 100644 index 00000000..4c420b47 --- /dev/null +++ b/experiments/exp3-dummy10-router/fledger-send.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --send-chat-msg ${FLEDGER_SEND_MSG} --timeout 600 +RemainAfterExit=yes +User=root +EnvironmentFile=/root/env.systemd + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/flsignal.service b/experiments/exp3-dummy10-router/flsignal.service new file mode 100644 index 00000000..787b6bad --- /dev/null +++ b/experiments/exp3-dummy10-router/flsignal.service @@ -0,0 +1,12 @@ +[Unit] +Description=Fledger signaling server +After=network.target + +[Service] +Type=oneshot +ExecStart=/root/flsignal -vvv +RemainAfterExit=yes +User=root + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/generate-env.sh b/experiments/exp3-dummy10-router/generate-env.sh new file mode 100755 index 00000000..6a61ffd6 --- /dev/null +++ b/experiments/exp3-dummy10-router/generate-env.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +mkdir -p env.systemd + +#amount=100 +amount=10 + +messages=() + +for i in $(seq $amount); do + message="$(openssl rand -hex 16)" + messages+=("$message") +done + +for i in $(seq 0 $((amount - 1))); do + j=$((("$i" + 2) % amount)) + nodename="n${i}" + envfile="env.systemd/${nodename}.env" + send_msg="${messages[$j]}" + recv_msg="${messages[$i]}" + + if test "$i" -lt 5; then + signalhost="10.0.0.128" + else + signalhost="10.0.1.128" + fi + + echo "FLEDGER_FLSIGNAL_HOST=${signalhost}" >"$envfile" + echo "FLEDGER_SEND_MSG=${send_msg}" >>"$envfile" + echo "FLEDGER_RECV_MSG=${recv_msg}" >>"$envfile" + + echo "[node $nodename]" + echo " <- ${recv_msg} [$i]" + echo " -> ${send_msg} [$j]" +done diff --git a/experiments/exp3-dummy10-router/hosts b/experiments/exp3-dummy10-router/hosts new file mode 100644 index 00000000..e50c21e2 --- /dev/null +++ b/experiments/exp3-dummy10-router/hosts @@ -0,0 +1,14 @@ +[nodes] +n0 +n1 +n2 +n3 +n4 +n5 +n6 +n7 +n8 +n9 + +[sig] +signaling diff --git a/experiments/exp3-dummy10-router/model.py b/experiments/exp3-dummy10-router/model.py new file mode 100644 index 00000000..09093d92 --- /dev/null +++ b/experiments/exp3-dummy10-router/model.py @@ -0,0 +1,34 @@ +from mergexp import * + +net = Network('exp3', routing == static) + +def makeNode(i: int): + name = f"n{i}" + return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) + +sna = [makeNode(i) for i in range(5)] +snb = [makeNode(i) for i in range(5, 10)] + +router = net.node('router', proc.cores>=1, memory.capacity>=mb(512)) +signaling = net.node('signaling', proc.cores>=2, memory.capacity>=mb(512)) + +sna.extend([router, signaling]) +snb.extend([router, signaling]) + +linka = net.connect(sna) +linkb = net.connect(snb) + +linka[router].socket.addrs = ip4("10.0.0.1/24") +linkb[router].socket.addrs = ip4("10.0.1.1/24") + +linka[signaling].socket.addrs = ip4("10.0.0.128/24") +linkb[signaling].socket.addrs = ip4("10.0.1.128/24") + + +for i in range(5): + suffix = str(i + 2) + linka[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") + linkb[snb[i]].socket.addrs = ip4(f"10.0.1.{suffix}/24") + + +experiment(net) diff --git a/experiments/exp3-dummy10-router/network.png b/experiments/exp3-dummy10-router/network.png new file mode 100644 index 0000000000000000000000000000000000000000..155f137fd3d6534875465f4398f90b4932540e25 GIT binary patch literal 12760 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 V!Pn^|Otljz+)$OxmAUiyUjXi{1KR)q literal 0 HcmV?d00001 diff --git a/experiments/exp3-dummy10-router/network.puml b/experiments/exp3-dummy10-router/network.puml new file mode 100644 index 00000000..cdb21450 --- /dev/null +++ b/experiments/exp3-dummy10-router/network.puml @@ -0,0 +1,31 @@ +@startuml +nwdiag { + network sna { + width = full + address = 10.0.0.0/24 + + n0 [address = 10.0.0.2]; + n1 [address = 10.0.0.3]; + n2 [address = 10.0.0.4]; + n3 [address = 10.0.0.5]; + n4 [address = 10.0.0.6]; + + router [address = 10.0.0.1]; + signaling [address = 10.0.0.128]; + } + + network snb { + address = 10.0.1.0/24 + + n5 [address = 10.0.1.2]; + n6 [address = 10.0.1.3]; + n7 [address = 10.0.1.4]; + n8 [address = 10.0.1.5]; + n9 [address = 10.0.1.6]; + + router [address = 10.0.1.1]; + signaling [address = 10.0.1.128]; + } +} +@enduml + diff --git a/experiments/exp3-dummy10-router/playbook.yaml b/experiments/exp3-dummy10-router/playbook.yaml new file mode 100644 index 00000000..6d8a948d --- /dev/null +++ b/experiments/exp3-dummy10-router/playbook.yaml @@ -0,0 +1,98 @@ +- name: Generate environments on ansible host + hosts: 127.0.0.1 + connection: local + tasks: + - name: Generate environemnts + changed_when: true + ansible.builtin.command: + cmd: ./generate-env.sh + +- name: Clean nodes and upload files (binaries and services) + hosts: nodes + become: true + tasks: + - name: Copy fledger binary to nodes + ansible.builtin.copy: + src: ~/musl/fledger + dest: ~/ + mode: preserve + + - name: Copy fledger service (send) + ansible.builtin.copy: + src: fledger-send.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Copy fledger service (recv) + ansible.builtin.copy: + src: fledger-recv.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Remove config folders + ansible.builtin.file: + path: /root/flnode + state: absent + +- name: Run signaling server on node signaling + hosts: signaling + become: true + tasks: + - name: Copy flsignal + ansible.builtin.copy: + src: ~/musl/flsignal + dest: ~/ + mode: preserve + - name: Copy signaling service + ansible.builtin.copy: + src: flsignal.service + dest: /etc/systemd/system/ + mode: preserve + - name: Start signaling server + ansible.builtin.systemd_service: + name: flsignal + state: restarted + daemon_reload: true + no_block: true + - name: Disable ip forwarding + ansible.posix.sysctl: + name: net.ipv4.ip_forward + value: 0 + sysctl_set: true + state: present + reload: true + +- name: Upload env and run fleger send / recv on each node + hosts: nodes + become: true + tasks: + - name: Copy env file to nodes + ansible.builtin.copy: + src: "env.systemd/{{ inventory_hostname }}.env" + dest: ~/env.systemd + mode: preserve + - name: Stop fledger node (recv) + ansible.builtin.systemd_service: + name: fledger-recv + state: stopped + daemon_reload: true + - name: Start fledger node (recv) + ansible.builtin.systemd_service: + name: fledger-recv + state: restarted + no_block: true + - name: Start fledger node (send) + ansible.builtin.systemd_service: + name: fledger-send + state: restarted + no_block: true + - name: Wait for fledger node (recv) to be ran + ansible.builtin.systemd: + name: fledger-recv + register: result + until: result.status['ExecMainCode'] == '1' + retries: 12 + delay: 5 + - name: Check exit code of fledger node + changed_when: false + ansible.builtin.command: '/bin/bash -c "test $(/usr/bin/systemctl show -P ExecMainStatus fledger-recv) -eq 0"' From b30cc9b545db55952688e579127a2ac3031e6ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sun, 30 Mar 2025 15:32:43 +0200 Subject: [PATCH 05/43] feat: remove timeout and use log for --recv-chat-msg (also rebase on upstream) --- cli/fledger/src/main.rs | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index cbfc25b5..dab4b0a9 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -1,4 +1,3 @@ -use anyhow::bail; use clap::{Parser, Subcommand}; use flarch::{ @@ -10,8 +9,6 @@ use flarch::{ use flmodules::{ dht_router::broker::DHTRouter, dht_storage::broker::DHTStorage, - dht_storage::{broker::DHTStorage, core::RealmConfig, realm_view::RealmView}, - flo::{crypto::FloVerifier, realm::Realm}, gossip_events::core::Event, network::{broker::NetworkIn, network_start, signal::SIGNAL_VERSION}, }; @@ -83,20 +80,14 @@ pub struct Args { #[arg(long, default_value = "false")] print_new_messages: bool, - /// Timeout after which the node exists with nonzero code - /// If the timeout is 0, then it is disabled. - #[arg(long, default_value = "0")] - timeout: u32, - /// Send a chat message upon node creation /// If the message is an empty string, ignore it. #[arg(long, default_value = "")] send_chat_msg: String, /// Wait for a chat message with the given body. - /// Exit with 0 code if the message is received. - /// To be combined with the --timeout option. /// If the message is an empty string, ignore it. + /// log "RECV_CHAT_MSG TRIGGERED" upon message received, at log level info #[arg(long, default_value = "")] recv_chat_msg: String, @@ -218,6 +209,10 @@ impl Fledger { .await?; } + if self.args.recv_chat_msg != "" { + log::info!("Waiting for chat message {}.", self.args.recv_chat_msg); + } + loop { count += 1; @@ -273,18 +268,10 @@ impl Fledger { .count() > 0 { - log::info!( - "Trigger message received: {}. Exiting.", - self.args.recv_chat_msg - ); - return Ok(()); + log::info!("RECV_CHAT_MSG TRIGGERED"); + self.args.recv_chat_msg = "".into(); } } - - // Handle --timeout - if timeout != 0 && timeout <= i { - bail!("Timeout reached."); - } } } From 0af068803c67aea9c02d4c56bbe55b798696e81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sun, 30 Mar 2025 15:33:10 +0200 Subject: [PATCH 06/43] chore: port exp1 and exp2 to new --recv mechanic --- experiments/exp1-dummy/fledger-recv.service | 3 +- experiments/exp1-dummy/fledger-send.service | 3 +- experiments/exp1-dummy/playbook.yaml | 22 ++++++++--- experiments/exp2-dummy10/fledger-recv.service | 3 +- experiments/exp2-dummy10/fledger-send.service | 3 +- experiments/exp2-dummy10/playbook.yaml | 37 +++++++++++-------- 6 files changed, 46 insertions(+), 25 deletions(-) diff --git a/experiments/exp1-dummy/fledger-recv.service b/experiments/exp1-dummy/fledger-recv.service index 254affa5..79f472d6 100644 --- a/experiments/exp1-dummy/fledger-recv.service +++ b/experiments/exp1-dummy/fledger-recv.service @@ -4,9 +4,10 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vvv -s ws://10.0.0.1:8765 --disable-turn-stun --print-new-messages --recv-chat-msg message --timeout 10 +ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun --print-new-messages --recv-chat-msg message RemainAfterExit=yes User=root +StandardOutput=append:/var/log/fledger-recv [Install] WantedBy=multi-user.target diff --git a/experiments/exp1-dummy/fledger-send.service b/experiments/exp1-dummy/fledger-send.service index 69fbd87e..f1eb3edb 100644 --- a/experiments/exp1-dummy/fledger-send.service +++ b/experiments/exp1-dummy/fledger-send.service @@ -4,9 +4,10 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vvv -s ws://10.0.0.1:8765 --disable-turn-stun --send-chat-msg message --timeout 10 +ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun --send-chat-msg message RemainAfterExit=yes User=root +StandardOutput=append:/var/log/fledger-send [Install] WantedBy=multi-user.target diff --git a/experiments/exp1-dummy/playbook.yaml b/experiments/exp1-dummy/playbook.yaml index 5a4bab4b..d1c230dc 100644 --- a/experiments/exp1-dummy/playbook.yaml +++ b/experiments/exp1-dummy/playbook.yaml @@ -4,14 +4,21 @@ tasks: - name: Copy executable to nodes ansible.builtin.copy: - src: ~/musl/ + src: "~/{{ item }}" dest: ~/ mode: preserve + loop: + - fledger + - flsignal - - name: Remove config folders + - name: Remove config folders and logs ansible.builtin.file: - path: /root/flnode + path: '{{ item }}' state: absent + loop: + - /root/flnode + - /var/log/fledger-recv + - /var/log/fledger-send - name: Setup node a hosts: a @@ -53,6 +60,9 @@ name: fledger-recv state: restarted daemon_reload: true - - name: Check exit code of fledger node - changed_when: false - ansible.builtin.command: '/bin/bash -c "test $(/usr/bin/systemctl show -P ExecMainStatus fledger-recv) -eq 0"' + no_block: true + - name: Wait for message received on fledger node (recv) + ansible.builtin.wait_for: + path: /var/log/fledger-recv + search_regex: "RECV_CHAT_MSG TRIGGERED" + timeout: 10 diff --git a/experiments/exp2-dummy10/fledger-recv.service b/experiments/exp2-dummy10/fledger-recv.service index 9aeeb54c..c46bb78d 100644 --- a/experiments/exp2-dummy10/fledger-recv.service +++ b/experiments/exp2-dummy10/fledger-recv.service @@ -4,10 +4,11 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --print-new-messages --recv-chat-msg ${FLEDGER_RECV_MSG} --timeout 10 +ExecStart=/root/fledger --config /root/flnode -vv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --print-new-messages --recv-chat-msg ${FLEDGER_RECV_MSG} RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger-recv [Install] WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/fledger-send.service b/experiments/exp2-dummy10/fledger-send.service index 14f8f277..2fb2203c 100644 --- a/experiments/exp2-dummy10/fledger-send.service +++ b/experiments/exp2-dummy10/fledger-send.service @@ -4,10 +4,11 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --send-chat-msg ${FLEDGER_SEND_MSG} --timeout 10 +ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --send-chat-msg ${FLEDGER_SEND_MSG} RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger-send [Install] WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/playbook.yaml b/experiments/exp2-dummy10/playbook.yaml index 62d686b8..6c4a1f57 100644 --- a/experiments/exp2-dummy10/playbook.yaml +++ b/experiments/exp2-dummy10/playbook.yaml @@ -13,26 +13,30 @@ tasks: - name: Copy executable to nodes ansible.builtin.copy: - src: ~/musl/ + src: "~/{{ item }}" dest: ~/ mode: preserve + loop: + - fledger + - flsignal - - name: Copy fledger service (send) + - name: Copy fledger services ansible.builtin.copy: - src: fledger-send.service + src: "{{ item }}" dest: /etc/systemd/system/ mode: preserve + loop: + - fledger-send.service + - fledger-recv.service - - name: Copy fledger service (recv) - ansible.builtin.copy: - src: fledger-recv.service - dest: /etc/systemd/system/ - mode: preserve - - - name: Remove config folders + - name: Remove config folders and logs ansible.builtin.file: - path: /root/flnode + path: '{{ item }}' state: absent + loop: + - /root/flnode + - /var/log/fledger-recv + - /var/log/fledger-send - name: Run signaling server on node A hosts: a @@ -64,11 +68,14 @@ name: fledger-send state: restarted no_block: true + daemon_reload: true - name: Start fledger node (recv) ansible.builtin.systemd_service: name: fledger-recv state: restarted - daemon_reload: true - - name: Check exit code of fledger node - changed_when: false - ansible.builtin.command: '/bin/bash -c "test $(/usr/bin/systemctl show -P ExecMainStatus fledger-recv) -eq 0"' + no_block: true + - name: Wait for message received on fledger node (recv) + ansible.builtin.wait_for: + path: /var/log/fledger-recv + search_regex: "RECV_CHAT_MSG TRIGGERED" + timeout: 10 From 20eebdbe6865b67d1f4c27a0572baf722ddee81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sun, 30 Mar 2025 17:09:42 +0200 Subject: [PATCH 07/43] feat(cli): specific command for simulation --- cli/fledger/src/main.rs | 103 +++++------------- cli/fledger/src/simulation.rs | 89 +++++++++++++++ experiments/exp1-dummy/fledger-recv.service | 2 +- experiments/exp1-dummy/fledger-send.service | 2 +- experiments/exp2-dummy10/fledger-recv.service | 2 +- experiments/exp2-dummy10/fledger-send.service | 2 +- .../exp3-dummy10-router/fledger-recv.service | 3 +- .../exp3-dummy10-router/fledger-send.service | 3 +- experiments/exp3-dummy10-router/playbook.yaml | 40 +++---- 9 files changed, 140 insertions(+), 106 deletions(-) create mode 100644 cli/fledger/src/simulation.rs diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index dab4b0a9..74e361e5 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -2,22 +2,22 @@ use clap::{Parser, Subcommand}; use flarch::{ data_storage::{DataStorage, DataStorageFile}, - nodeids::U256, tasks::wait_ms, web_rtc::connection::{ConnectionConfig, HostLogin}, }; use flmodules::{ dht_router::broker::DHTRouter, dht_storage::broker::DHTStorage, - gossip_events::core::Event, network::{broker::NetworkIn, network_start, signal::SIGNAL_VERSION}, }; use flnode::{node::Node, version::VERSION_STRING}; use page::{Page, PageCommands}; use realm::{RealmCommands, RealmHandler}; +use simulation::{SimulationCommands, SimulationHandler}; mod page; mod realm; +mod simulation; /// Fledger node CLI binary #[derive(Parser, Debug, Clone)] @@ -64,6 +64,10 @@ pub struct Args { #[arg(long, default_value = "false")] log_gossip: bool, + /// Log dht connections + #[arg(long, default_value = "false")] + log_dht_connections: bool, + /// Log random router #[arg(long, default_value = "false")] log_random: bool, @@ -76,21 +80,6 @@ pub struct Args { #[arg(long, default_value = "false")] log_dht_storage: bool, - /// Print new messages as they come - #[arg(long, default_value = "false")] - print_new_messages: bool, - - /// Send a chat message upon node creation - /// If the message is an empty string, ignore it. - #[arg(long, default_value = "")] - send_chat_msg: String, - - /// Wait for a chat message with the given body. - /// If the message is an empty string, ignore it. - /// log "RECV_CHAT_MSG TRIGGERED" upon message received, at log level info - #[arg(long, default_value = "")] - recv_chat_msg: String, - #[command(subcommand)] command: Option, } @@ -111,6 +100,11 @@ enum Commands { Crypto {}, /// Prints the statistics of the different modules and then quits Stats {}, + /// Simulation tasks + Simulation { + #[command(subcommand)] + command: SimulationCommands, + }, } struct Fledger { @@ -118,7 +112,6 @@ struct Fledger { ds: DHTStorage, dr: DHTRouter, args: Args, - acked_msg_ids: Vec, } #[tokio::main] @@ -175,7 +168,6 @@ impl Fledger { dr: node.dht_router.as_ref().unwrap().clone(), node, args, - acked_msg_ids: Vec::new(), }; match f.args.command.clone() { @@ -184,6 +176,7 @@ impl Fledger { Commands::Crypto {} => todo!(), Commands::Stats {} => todo!(), Commands::Page { command } => Page::run(f, command).await, + Commands::Simulation { command } => SimulationHandler::run(f, command).await, }, None => f.loop_node(FledgerState::Forever).await, } @@ -202,17 +195,6 @@ impl Fledger { FledgerState::Forever => log::info!("Looping forever"), } - // Handle --send-chat-msg - if self.args.send_chat_msg != "" { - self.node - .add_chat_message(self.args.send_chat_msg.clone()) - .await?; - } - - if self.args.recv_chat_msg != "" { - log::info!("Waiting for chat message {}.", self.args.recv_chat_msg); - } - loop { count += 1; @@ -242,35 +224,19 @@ impl Fledger { } self.ds.sync()?; - println!( - "dht-connections: {}/{}", - self.dr.stats.borrow().active, - self.dr - .stats - .borrow() - .all_nodes - .iter() - .map(|n| n.to_string()) - .collect::>() - .join(", ") - ); - - // Handle --recv-chat-msg - if self.args.recv_chat_msg != "" { - if self - .node - .gossip - .as_ref() - .unwrap() - .chat_events() - .iter() - .filter(|ev| ev.msg == self.args.recv_chat_msg) - .count() - > 0 - { - log::info!("RECV_CHAT_MSG TRIGGERED"); - self.args.recv_chat_msg = "".into(); - } + if self.args.log_dht_connections { + log::info!( + "dht-connections: {}/{}", + self.dr.stats.borrow().active, + self.dr + .stats + .borrow() + .all_nodes + .iter() + .map(|n| n.to_string()) + .collect::>() + .join(", ") + ); } } } @@ -317,25 +283,6 @@ impl Fledger { ); } } - - // Handle --print-new-messages - if self.args.print_new_messages { - let chat_events = self.node.gossip.as_ref().unwrap().chat_events(); - let chats: Vec<&Event> = chat_events - .iter() - .filter(|ev| !self.acked_msg_ids.contains(&ev.get_id())) - .collect(); - - if chats.len() <= 0 { - log::debug!("... No new message"); - } else { - log::info!("--- New Messages ---"); - for chat in chats { - self.acked_msg_ids.push(chat.get_id()); - log::info!(" [{}] {}", chat.src, chat.msg); - } - } - } } Ok(()) diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs new file mode 100644 index 00000000..06e80815 --- /dev/null +++ b/cli/fledger/src/simulation.rs @@ -0,0 +1,89 @@ +use clap::{arg, Subcommand}; +use flarch::{nodeids::U256, tasks::wait_ms}; +use flmodules::gossip_events::core::Event; + +use crate::Fledger; + +#[derive(Subcommand, Debug, Clone)] +pub enum SimulationCommands { + /// Send a chat message upon node creation + SendChat { msg: String }, + /// Wait for a chat message with the given body. + /// log "RECV_CHAT_MSG TRIGGERED" upon message received, at log level info + RecvChat { + msg: String, + + /// Print new messages as they come + #[arg(long, default_value = "false")] + print_new_messages: bool, + }, +} + +pub struct SimulationHandler {} + +impl SimulationHandler { + pub async fn run(f: Fledger, command: SimulationCommands) -> anyhow::Result<()> { + match command { + SimulationCommands::SendChat { msg } => Self::simulation_send_chat(f, msg).await, + SimulationCommands::RecvChat { + msg, + print_new_messages, + } => Self::simulation_recv_chat(f, msg, print_new_messages).await, + } + } + + async fn simulation_send_chat(mut f: Fledger, msg: String) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::Connected(1)).await?; + + log::info!("Sending chat message {}.", msg); + f.node.add_chat_message(msg).await?; + + f.loop_node(crate::FledgerState::Forever).await?; + Ok(()) + } + + async fn simulation_recv_chat( + mut f: Fledger, + msg: String, + print_new_messages: bool, + ) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::Connected(1)).await?; + + log::info!("Waiting for chat message {}.", msg); + + let mut acked_msg_ids: Vec = Vec::new(); + + loop { + wait_ms(1000).await; + + if print_new_messages { + Self::log_new_messages(&f, &mut acked_msg_ids); + } + + let gossip = f.node.gossip.as_ref(); + if gossip.unwrap().chat_events().iter().any(|ev| ev.msg == msg) { + log::info!("RECV_CHAT_MSG TRIGGERED"); + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } + } + } + + fn log_new_messages(f: &Fledger, acked_msg_ids: &mut Vec) { + let chat_events = f.node.gossip.as_ref().unwrap().chat_events(); + let chats: Vec<&Event> = chat_events + .iter() + .filter(|ev| !acked_msg_ids.contains(&ev.get_id())) + .collect(); + + if chats.len() <= 0 { + log::debug!("... No new message"); + } else { + log::info!("--- New Messages ---"); + for chat in chats { + acked_msg_ids.push(chat.get_id()); + log::info!(" [{}] {}", chat.src, chat.msg); + } + } + } +} diff --git a/experiments/exp1-dummy/fledger-recv.service b/experiments/exp1-dummy/fledger-recv.service index 79f472d6..370b78ca 100644 --- a/experiments/exp1-dummy/fledger-recv.service +++ b/experiments/exp1-dummy/fledger-recv.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun --print-new-messages --recv-chat-msg message +ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun simulation recv-chat "message" --print-new-messages RemainAfterExit=yes User=root StandardOutput=append:/var/log/fledger-recv diff --git a/experiments/exp1-dummy/fledger-send.service b/experiments/exp1-dummy/fledger-send.service index f1eb3edb..20d95893 100644 --- a/experiments/exp1-dummy/fledger-send.service +++ b/experiments/exp1-dummy/fledger-send.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun --send-chat-msg message +ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun simulation send-chat "message" RemainAfterExit=yes User=root StandardOutput=append:/var/log/fledger-send diff --git a/experiments/exp2-dummy10/fledger-recv.service b/experiments/exp2-dummy10/fledger-recv.service index c46bb78d..3d09ffc6 100644 --- a/experiments/exp2-dummy10/fledger-recv.service +++ b/experiments/exp2-dummy10/fledger-recv.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --print-new-messages --recv-chat-msg ${FLEDGER_RECV_MSG} +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation recv-chat ${FLEDGER_RECV_MSG} --print-new-messages RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd diff --git a/experiments/exp2-dummy10/fledger-send.service b/experiments/exp2-dummy10/fledger-send.service index 2fb2203c..bd92bc6c 100644 --- a/experiments/exp2-dummy10/fledger-send.service +++ b/experiments/exp2-dummy10/fledger-send.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --send-chat-msg ${FLEDGER_SEND_MSG} +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation send-chat ${FLEDGER_SEND_MSG} RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd diff --git a/experiments/exp3-dummy10-router/fledger-recv.service b/experiments/exp3-dummy10-router/fledger-recv.service index 9acc2588..35adeb5a 100644 --- a/experiments/exp3-dummy10-router/fledger-recv.service +++ b/experiments/exp3-dummy10-router/fledger-recv.service @@ -4,10 +4,11 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --print-new-messages --recv-chat-msg ${FLEDGER_RECV_MSG} --timeout 60 +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation recv-chat --print-new-messages ${FLEDGER_RECV_MSG} RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger-recv [Install] WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/fledger-send.service b/experiments/exp3-dummy10-router/fledger-send.service index 4c420b47..bd92bc6c 100644 --- a/experiments/exp3-dummy10-router/fledger-send.service +++ b/experiments/exp3-dummy10-router/fledger-send.service @@ -4,10 +4,11 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vvv -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun --send-chat-msg ${FLEDGER_SEND_MSG} --timeout 600 +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation send-chat ${FLEDGER_SEND_MSG} RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger-send [Install] WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/playbook.yaml b/experiments/exp3-dummy10-router/playbook.yaml index 6d8a948d..2598545c 100644 --- a/experiments/exp3-dummy10-router/playbook.yaml +++ b/experiments/exp3-dummy10-router/playbook.yaml @@ -13,26 +13,27 @@ tasks: - name: Copy fledger binary to nodes ansible.builtin.copy: - src: ~/musl/fledger + src: ~/fledger dest: ~/ mode: preserve - - name: Copy fledger service (send) + - name: Copy fledger services ansible.builtin.copy: - src: fledger-send.service + src: "{{ item }}" dest: /etc/systemd/system/ mode: preserve + loop: + - fledger-send.service + - fledger-recv.service - - name: Copy fledger service (recv) - ansible.builtin.copy: - src: fledger-recv.service - dest: /etc/systemd/system/ - mode: preserve - - - name: Remove config folders + - name: Remove config folders and log files ansible.builtin.file: - path: /root/flnode + path: "{{ item }}" state: absent + loop: + - /root/flnode + - /var/log/fledger-recv + - /var/log/fledger-send - name: Run signaling server on node signaling hosts: signaling @@ -40,7 +41,7 @@ tasks: - name: Copy flsignal ansible.builtin.copy: - src: ~/musl/flsignal + src: ~/flsignal dest: ~/ mode: preserve - name: Copy signaling service @@ -86,13 +87,8 @@ name: fledger-send state: restarted no_block: true - - name: Wait for fledger node (recv) to be ran - ansible.builtin.systemd: - name: fledger-recv - register: result - until: result.status['ExecMainCode'] == '1' - retries: 12 - delay: 5 - - name: Check exit code of fledger node - changed_when: false - ansible.builtin.command: '/bin/bash -c "test $(/usr/bin/systemctl show -P ExecMainStatus fledger-recv) -eq 0"' + - name: Wait for message received on fledger node (recv) + ansible.builtin.wait_for: + path: /var/log/fledger-recv + search_regex: "RECV_CHAT_MSG TRIGGERED" + timeout: 600 From 08e05689e3490f675891b2fc46d580503f3956aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sun, 30 Mar 2025 18:10:22 +0200 Subject: [PATCH 08/43] feat: random waiting time for simulations --- cli/fledger/src/main.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 74e361e5..4e2f07bf 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -2,6 +2,7 @@ use clap::{Parser, Subcommand}; use flarch::{ data_storage::{DataStorage, DataStorageFile}, + random, tasks::wait_ms, web_rtc::connection::{ConnectionConfig, HostLogin}, }; @@ -135,6 +136,20 @@ async fn main() -> anyhow::Result<()> { VERSION_STRING ); + // wait a random amount of time before running a simulation + // to avoid overloading the signaling server + match args.command.clone() { + Some(cmd) => match cmd { + Commands::Simulation { command: _ } => { + let randtime: u64 = random::() % 5000; + log::info!("Waiting {}ms before running this node...", randtime); + wait_ms(randtime).await; + } + _ => {} + }, + _ => {} + } + let cc = if args.disable_turn_stun { ConnectionConfig::from_signal(&args.signal_url) } else { From 406f882d31bbc46b818aa47aa9d132a832b362ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 12:03:06 +0200 Subject: [PATCH 09/43] chore: rename signaling host to central host (exp3) --- experiments/README.md | 5 ++++- experiments/exp3-dummy10-router/README.md | 2 +- experiments/exp3-dummy10-router/fledger-recv.service | 2 +- experiments/exp3-dummy10-router/fledger-send.service | 2 +- experiments/exp3-dummy10-router/generate-env.sh | 2 +- experiments/exp3-dummy10-router/hosts | 4 ++-- experiments/exp3-dummy10-router/model.py | 10 +++++----- experiments/exp3-dummy10-router/network.puml | 4 ++-- experiments/exp3-dummy10-router/playbook.yaml | 4 ++-- 9 files changed, 19 insertions(+), 16 deletions(-) diff --git a/experiments/README.md b/experiments/README.md index c19d4621..5c04d278 100644 --- a/experiments/README.md +++ b/experiments/README.md @@ -29,7 +29,7 @@ Check `exp1-dummy` for an example of both Read the documentation and do the hello world. Then, create one XDC to be used for all experiments. -Use this configuration (read the documentation before using it): +Use this SSH configuration (read the documentation before using it): ```bash Host sphere @@ -50,6 +50,9 @@ Don't forget to replace the values for `YOUR_USER` and `SSH_NAME_FROM_SPHERE`. On the XDC, install ansible (use the ansible/ansible apt ppa!). +Then install ansible's prometheus collection with +`ansible-galaxy collection install prometheus.prometheus` + Configure ansible with this `~/.ansible.cfg` from the documentation : ```bash diff --git a/experiments/exp3-dummy10-router/README.md b/experiments/exp3-dummy10-router/README.md index 8f816005..4be4a08d 100644 --- a/experiments/exp3-dummy10-router/README.md +++ b/experiments/exp3-dummy10-router/README.md @@ -3,7 +3,7 @@ Same as exp2 but with two lans of 5 nodes separated by a router - Timeout for send is 10min, timeout for recv is 60sec. -- There is a separate signal server +- There is a central server hosting the signaling server and prometheus No nice plantuml diagram for the experiment here, it's the same as exp2 but the topology changes. diff --git a/experiments/exp3-dummy10-router/fledger-recv.service b/experiments/exp3-dummy10-router/fledger-recv.service index 35adeb5a..f1285f12 100644 --- a/experiments/exp3-dummy10-router/fledger-recv.service +++ b/experiments/exp3-dummy10-router/fledger-recv.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation recv-chat --print-new-messages ${FLEDGER_RECV_MSG} +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_CENTRAL_HOST}:8765 --disable-turn-stun simulation recv-chat --print-new-messages ${FLEDGER_RECV_MSG} RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd diff --git a/experiments/exp3-dummy10-router/fledger-send.service b/experiments/exp3-dummy10-router/fledger-send.service index bd92bc6c..f1457096 100644 --- a/experiments/exp3-dummy10-router/fledger-send.service +++ b/experiments/exp3-dummy10-router/fledger-send.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation send-chat ${FLEDGER_SEND_MSG} +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_CENTRAL_HOST}:8765 --disable-turn-stun simulation send-chat ${FLEDGER_SEND_MSG} RemainAfterExit=yes User=root EnvironmentFile=/root/env.systemd diff --git a/experiments/exp3-dummy10-router/generate-env.sh b/experiments/exp3-dummy10-router/generate-env.sh index 6a61ffd6..9aa1a65e 100755 --- a/experiments/exp3-dummy10-router/generate-env.sh +++ b/experiments/exp3-dummy10-router/generate-env.sh @@ -25,7 +25,7 @@ for i in $(seq 0 $((amount - 1))); do signalhost="10.0.1.128" fi - echo "FLEDGER_FLSIGNAL_HOST=${signalhost}" >"$envfile" + echo "FLEDGER_CENTRAL_HOST=${signalhost}" >"$envfile" echo "FLEDGER_SEND_MSG=${send_msg}" >>"$envfile" echo "FLEDGER_RECV_MSG=${recv_msg}" >>"$envfile" diff --git a/experiments/exp3-dummy10-router/hosts b/experiments/exp3-dummy10-router/hosts index e50c21e2..9bc112bc 100644 --- a/experiments/exp3-dummy10-router/hosts +++ b/experiments/exp3-dummy10-router/hosts @@ -10,5 +10,5 @@ n7 n8 n9 -[sig] -signaling +[central] +central diff --git a/experiments/exp3-dummy10-router/model.py b/experiments/exp3-dummy10-router/model.py index 09093d92..0e1508d5 100644 --- a/experiments/exp3-dummy10-router/model.py +++ b/experiments/exp3-dummy10-router/model.py @@ -10,10 +10,10 @@ def makeNode(i: int): snb = [makeNode(i) for i in range(5, 10)] router = net.node('router', proc.cores>=1, memory.capacity>=mb(512)) -signaling = net.node('signaling', proc.cores>=2, memory.capacity>=mb(512)) +central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) -sna.extend([router, signaling]) -snb.extend([router, signaling]) +sna.extend([router, central]) +snb.extend([router, central]) linka = net.connect(sna) linkb = net.connect(snb) @@ -21,8 +21,8 @@ def makeNode(i: int): linka[router].socket.addrs = ip4("10.0.0.1/24") linkb[router].socket.addrs = ip4("10.0.1.1/24") -linka[signaling].socket.addrs = ip4("10.0.0.128/24") -linkb[signaling].socket.addrs = ip4("10.0.1.128/24") +linka[central].socket.addrs = ip4("10.0.0.128/24") +linkb[central].socket.addrs = ip4("10.0.1.128/24") for i in range(5): diff --git a/experiments/exp3-dummy10-router/network.puml b/experiments/exp3-dummy10-router/network.puml index cdb21450..a688354d 100644 --- a/experiments/exp3-dummy10-router/network.puml +++ b/experiments/exp3-dummy10-router/network.puml @@ -11,7 +11,7 @@ nwdiag { n4 [address = 10.0.0.6]; router [address = 10.0.0.1]; - signaling [address = 10.0.0.128]; + central [address = 10.0.0.128]; } network snb { @@ -24,7 +24,7 @@ nwdiag { n9 [address = 10.0.1.6]; router [address = 10.0.1.1]; - signaling [address = 10.0.1.128]; + central [address = 10.0.1.128]; } } @enduml diff --git a/experiments/exp3-dummy10-router/playbook.yaml b/experiments/exp3-dummy10-router/playbook.yaml index 2598545c..7260a90f 100644 --- a/experiments/exp3-dummy10-router/playbook.yaml +++ b/experiments/exp3-dummy10-router/playbook.yaml @@ -35,8 +35,8 @@ - /var/log/fledger-recv - /var/log/fledger-send -- name: Run signaling server on node signaling - hosts: signaling +- name: Run signaling server on central node + hosts: central become: true tasks: - name: Copy flsignal From 044abca07a96d479cf9ad4b14fbeac2c7d2ca07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 12:03:36 +0200 Subject: [PATCH 10/43] feat: allow to bypass wait time for simulations --- cli/fledger/src/main.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 4e2f07bf..f54aeee8 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -16,6 +16,7 @@ use page::{Page, PageCommands}; use realm::{RealmCommands, RealmHandler}; use simulation::{SimulationCommands, SimulationHandler}; +mod metrics; mod page; mod realm; mod simulation; @@ -81,6 +82,11 @@ pub struct Args { #[arg(long, default_value = "false")] log_dht_storage: bool, + /// Do not wait for a random amount + /// of ms if the command is simulation + #[arg(long, default_value = "false")] + no_simulation_wait: bool, + #[command(subcommand)] command: Option, } @@ -138,16 +144,18 @@ async fn main() -> anyhow::Result<()> { // wait a random amount of time before running a simulation // to avoid overloading the signaling server - match args.command.clone() { - Some(cmd) => match cmd { - Commands::Simulation { command: _ } => { - let randtime: u64 = random::() % 5000; - log::info!("Waiting {}ms before running this node...", randtime); - wait_ms(randtime).await; - } + if !args.no_simulation_wait { + match args.command.clone() { + Some(cmd) => match cmd { + Commands::Simulation { command: _ } => { + let randtime: u64 = random::() % 5000; + log::info!("Waiting {}ms before running this node...", randtime); + wait_ms(randtime).await; + } + _ => {} + }, _ => {} - }, - _ => {} + } } let cc = if args.disable_turn_stun { From 5b9381e6a189d6c0aeb8886e60596ac25264b740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 15:03:09 +0200 Subject: [PATCH 11/43] feat: metrics for simulations --- .gitmodules | 3 +++ cli/fledger/Cargo.toml | 4 +++- cli/fledger/src/metrics.rs | 19 +++++++++++++++++++ cli/fledger/src/simulation.rs | 22 +++++++++++++++++++--- metrics-exporter-influx | 1 + 5 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 .gitmodules create mode 100644 cli/fledger/src/metrics.rs create mode 160000 metrics-exporter-influx diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..cafd2d66 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "metrics-exporter-influx"] + path = metrics-exporter-influx + url = https://github.com/aizensoosuke/metrics-exporter-influx.git diff --git a/cli/fledger/Cargo.toml b/cli/fledger/Cargo.toml index 82570990..61dc5de1 100644 --- a/cli/fledger/Cargo.toml +++ b/cli/fledger/Cargo.toml @@ -17,7 +17,7 @@ flmodules = { path = "../../flmodules", version = "0.9" } flnode = { path = "../../flnode", version = "0.9" } flcrypto = { path = "../../flcrypto", version = "0.9" } -anyhow = {version = "1", features = ["backtrace"]} +anyhow = { version = "1", features = ["backtrace"] } clap = "4" clap-verbosity-flag = "3" env_logger = "0.11" @@ -25,3 +25,5 @@ log = "0.4" thiserror = "2" tokio = "1" webrtc-util = "0.10" +metrics = "0.21.1" +metrics-exporter-influx = { version = "0.2.2", path = "../../metrics-exporter-influx" } diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs new file mode 100644 index 00000000..b4c82a1f --- /dev/null +++ b/cli/fledger/src/metrics.rs @@ -0,0 +1,19 @@ +use std::{fs::File, time::Duration}; + +use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; + +pub struct Metrics {} + +impl Metrics { + pub fn setup(node_name: String) -> InfluxRecorderHandle { + log::info!("Setting up metrics"); + let metrics_file = + File::create("/tmp/out.metrics").expect("could not create /tmp/out.metrics"); + return InfluxBuilder::new() + .with_duration(Duration::from_secs(1)) + .with_writer(metrics_file) + .add_global_tag("node_name", node_name) + .install() + .expect("could not setup influx recorder"); + } +} diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 06e80815..70b90d8d 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,8 +1,8 @@ +use crate::{metrics::Metrics, Fledger}; use clap::{arg, Subcommand}; use flarch::{nodeids::U256, tasks::wait_ms}; use flmodules::gossip_events::core::Event; - -use crate::Fledger; +use metrics::{absolute_counter, increment_counter}; #[derive(Subcommand, Debug, Clone)] pub enum SimulationCommands { @@ -16,6 +16,10 @@ pub enum SimulationCommands { /// Print new messages as they come #[arg(long, default_value = "false")] print_new_messages: bool, + + /// Node's name + #[arg(long, default_value = "unknown")] + node_name: String, }, } @@ -28,7 +32,8 @@ impl SimulationHandler { SimulationCommands::RecvChat { msg, print_new_messages, - } => Self::simulation_recv_chat(f, msg, print_new_messages).await, + node_name, + } => Self::simulation_recv_chat(f, msg, print_new_messages, node_name).await, } } @@ -46,6 +51,7 @@ impl SimulationHandler { mut f: Fledger, msg: String, print_new_messages: bool, + node_name: String, ) -> anyhow::Result<()> { f.loop_node(crate::FledgerState::Connected(1)).await?; @@ -53,9 +59,19 @@ impl SimulationHandler { let mut acked_msg_ids: Vec = Vec::new(); + // necessary to grab the variable for lifetime purposes. + let _influx = Metrics::setup(node_name); + loop { wait_ms(1000).await; + let fledger_message_total = f.node.gossip.as_ref().unwrap().chat_events().len(); + absolute_counter!( + "fledger_message_total", + fledger_message_total.try_into().unwrap() + ); + increment_counter!("fledger_iterations_total"); + if print_new_messages { Self::log_new_messages(&f, &mut acked_msg_ids); } diff --git a/metrics-exporter-influx b/metrics-exporter-influx new file mode 160000 index 00000000..e79ff4ac --- /dev/null +++ b/metrics-exporter-influx @@ -0,0 +1 @@ +Subproject commit e79ff4ac29d34e3593cad950d6693b8684841b34 From 1fa4d453841cdd56f611c318f0f8b21e594df40e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 20:36:57 +0200 Subject: [PATCH 12/43] feat: --log-random connected nodes instead of online nodes --- cli/fledger/src/main.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index f54aeee8..8173d4bd 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -14,7 +14,7 @@ use flmodules::{ use flnode::{node::Node, version::VERSION_STRING}; use page::{Page, PageCommands}; use realm::{RealmCommands, RealmHandler}; -use simulation::{SimulationCommands, SimulationHandler}; +use simulation::{SimulationCommand, SimulationHandler}; mod metrics; mod page; @@ -108,10 +108,7 @@ enum Commands { /// Prints the statistics of the different modules and then quits Stats {}, /// Simulation tasks - Simulation { - #[command(subcommand)] - command: SimulationCommands, - }, + Simulation(SimulationCommand), } struct Fledger { @@ -147,7 +144,7 @@ async fn main() -> anyhow::Result<()> { if !args.no_simulation_wait { match args.command.clone() { Some(cmd) => match cmd { - Commands::Simulation { command: _ } => { + Commands::Simulation(_) => { let randtime: u64 = random::() % 5000; log::info!("Waiting {}ms before running this node...", randtime); wait_ms(randtime).await; @@ -199,7 +196,7 @@ impl Fledger { Commands::Crypto {} => todo!(), Commands::Stats {} => todo!(), Commands::Page { command } => Page::run(f, command).await, - Commands::Simulation { command } => SimulationHandler::run(f, command).await, + Commands::Simulation(command) => SimulationHandler::run(f, command).await, }, None => f.loop_node(FledgerState::Forever).await, } @@ -270,7 +267,7 @@ impl Fledger { log::info!( "Nodes are: {}", self.node - .nodes_online()? + .nodes_connected()? .iter() .map(|n| format!("{}/{}", n.name, n.get_id())) .collect::>() From b161bbd94ecb5d2f749b84a70a7b5add48dd99f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 20:37:49 +0200 Subject: [PATCH 13/43] feat(cli): merge send and recv commands --- cli/fledger/src/simulation.rs | 98 +++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 70b90d8d..4c2a264d 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,61 +1,62 @@ use crate::{metrics::Metrics, Fledger}; -use clap::{arg, Subcommand}; +use clap::{arg, Args, Subcommand}; use flarch::{nodeids::U256, tasks::wait_ms}; use flmodules::gossip_events::core::Event; use metrics::{absolute_counter, increment_counter}; +#[derive(Args, Debug, Clone)] +pub struct SimulationCommand { + /// Print new messages as they come + #[arg(long, default_value = "false")] + print_new_messages: bool, + + #[command(subcommand)] + pub subcommand: SimulationSubcommand, +} + #[derive(Subcommand, Debug, Clone)] -pub enum SimulationCommands { - /// Send a chat message upon node creation - SendChat { msg: String }, - /// Wait for a chat message with the given body. - /// log "RECV_CHAT_MSG TRIGGERED" upon message received, at log level info - RecvChat { - msg: String, - - /// Print new messages as they come - #[arg(long, default_value = "false")] - print_new_messages: bool, - - /// Node's name - #[arg(long, default_value = "unknown")] - node_name: String, +pub enum SimulationSubcommand { + Chat { + /// Send a chat message upon node creation + #[arg(long)] + send_msg: Option, + + /// Wait for a chat message with the given body. + /// log "RECV_CHAT_MSG TRIGGERED" upon message received, at log level info + #[arg(long)] + recv_msg: Option, }, } pub struct SimulationHandler {} impl SimulationHandler { - pub async fn run(f: Fledger, command: SimulationCommands) -> anyhow::Result<()> { - match command { - SimulationCommands::SendChat { msg } => Self::simulation_send_chat(f, msg).await, - SimulationCommands::RecvChat { - msg, - print_new_messages, - node_name, - } => Self::simulation_recv_chat(f, msg, print_new_messages, node_name).await, + pub async fn run(f: Fledger, command: SimulationCommand) -> anyhow::Result<()> { + match command.subcommand.clone() { + SimulationSubcommand::Chat { send_msg, recv_msg } => { + Self::run_chat(f, command, send_msg, recv_msg).await + } } } - async fn simulation_send_chat(mut f: Fledger, msg: String) -> anyhow::Result<()> { - f.loop_node(crate::FledgerState::Connected(1)).await?; - - log::info!("Sending chat message {}.", msg); - f.node.add_chat_message(msg).await?; - - f.loop_node(crate::FledgerState::Forever).await?; - Ok(()) - } - - async fn simulation_recv_chat( + async fn run_chat( mut f: Fledger, - msg: String, - print_new_messages: bool, - node_name: String, + simulation_args: SimulationCommand, + send_msg: Option, + recv_msg: Option, ) -> anyhow::Result<()> { f.loop_node(crate::FledgerState::Connected(1)).await?; - log::info!("Waiting for chat message {}.", msg); + let node_name = f.args.name.clone().unwrap_or("unknown".into()); + + if let Some(ref msg) = recv_msg { + log::info!("Waiting for chat message {}.", msg); + } + + if let Some(ref msg) = send_msg { + log::info!("Sending chat message {}.", msg); + f.node.add_chat_message(msg.into()).await?; + } let mut acked_msg_ids: Vec = Vec::new(); @@ -72,15 +73,22 @@ impl SimulationHandler { ); increment_counter!("fledger_iterations_total"); - if print_new_messages { + if simulation_args.print_new_messages { Self::log_new_messages(&f, &mut acked_msg_ids); } - let gossip = f.node.gossip.as_ref(); - if gossip.unwrap().chat_events().iter().any(|ev| ev.msg == msg) { - log::info!("RECV_CHAT_MSG TRIGGERED"); - f.loop_node(crate::FledgerState::Forever).await?; - return Ok(()); + if let Some(ref msg) = recv_msg { + let gossip = f.node.gossip.as_ref(); + if gossip + .unwrap() + .chat_events() + .iter() + .any(|ev| ev.msg.eq(msg)) + { + log::info!("RECV_CHAT_MSG TRIGGERED"); + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } } } } From 5eb76d375e190d0e0380d5c34de964812f86f3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 20:38:26 +0200 Subject: [PATCH 14/43] fix(exp2): single instance, wait for flsignal restart --- experiments/exp2-dummy10/README.md | 2 +- experiments/exp2-dummy10/explanation.puml | 7 ++---- experiments/exp2-dummy10/fledger-recv.service | 14 ----------- experiments/exp2-dummy10/fledger-send.service | 14 ----------- experiments/exp2-dummy10/fledger.service | 15 +++++++++++ experiments/exp2-dummy10/flsignal.service | 7 +++--- experiments/exp2-dummy10/generate-env.sh | 1 + experiments/exp2-dummy10/model.py | 2 +- experiments/exp2-dummy10/playbook.yaml | 25 +++++++------------ 9 files changed, 33 insertions(+), 54 deletions(-) delete mode 100644 experiments/exp2-dummy10/fledger-recv.service delete mode 100644 experiments/exp2-dummy10/fledger-send.service create mode 100644 experiments/exp2-dummy10/fledger.service diff --git a/experiments/exp2-dummy10/README.md b/experiments/exp2-dummy10/README.md index c1851ef1..d55118a0 100644 --- a/experiments/exp2-dummy10/README.md +++ b/experiments/exp2-dummy10/README.md @@ -6,4 +6,4 @@ Each node broadcasts a message (destined to a single node), and waits for a message from one specific other node. The playbook succeeds if all nodes receive their message, -and fails if the timeout of 10s is reached. +and fails if the timeout of 120s is reached. diff --git a/experiments/exp2-dummy10/explanation.puml b/experiments/exp2-dummy10/explanation.puml index 98890000..6bc2e2ff 100644 --- a/experiments/exp2-dummy10/explanation.puml +++ b/experiments/exp2-dummy10/explanation.puml @@ -12,10 +12,7 @@ participant "Node J (10)" as J hnote over A: signaling server hnote across - fledger (send) -endhnote -hnote across - fledger (recv) + fledger (send-recv) endhnote rnote across Setup with signaling server @@ -33,7 +30,7 @@ I -> A: **broadcast** //"ItoA"// J -> B: **broadcast** //"JtoB"// rnote across - Timeout 10s + Timeout 120s endrnote rnote across //exit code 0// diff --git a/experiments/exp2-dummy10/fledger-recv.service b/experiments/exp2-dummy10/fledger-recv.service deleted file mode 100644 index 3d09ffc6..00000000 --- a/experiments/exp2-dummy10/fledger-recv.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=oneshot -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation recv-chat ${FLEDGER_RECV_MSG} --print-new-messages -RemainAfterExit=yes -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger-recv - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/fledger-send.service b/experiments/exp2-dummy10/fledger-send.service deleted file mode 100644 index bd92bc6c..00000000 --- a/experiments/exp2-dummy10/fledger-send.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=oneshot -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --disable-turn-stun simulation send-chat ${FLEDGER_SEND_MSG} -RemainAfterExit=yes -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger-send - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/fledger.service b/experiments/exp2-dummy10/fledger.service new file mode 100644 index 00000000..bdaaf60a --- /dev/null +++ b/experiments/exp2-dummy10/fledger.service @@ -0,0 +1,15 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --name ${FLEDGER_NODE_NAME} --disable-turn-stun simulation --print-new-messages chat --send-msg ${FLEDGER_SEND_MSG} --recv-msg ${FLEDGER_RECV_MSG} +RemainAfterExit=yes +Restart=no +User=root +EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/flsignal.service b/experiments/exp2-dummy10/flsignal.service index 787b6bad..c1ea0035 100644 --- a/experiments/exp2-dummy10/flsignal.service +++ b/experiments/exp2-dummy10/flsignal.service @@ -3,10 +3,11 @@ Description=Fledger signaling server After=network.target [Service] -Type=oneshot -ExecStart=/root/flsignal -vvv -RemainAfterExit=yes +Type=simple +ExecStart=/root/flsignal -v User=root +Restart=no +StandardOutput=append:/var/log/flsignal [Install] WantedBy=multi-user.target diff --git a/experiments/exp2-dummy10/generate-env.sh b/experiments/exp2-dummy10/generate-env.sh index cebf4a15..881fdf2e 100755 --- a/experiments/exp2-dummy10/generate-env.sh +++ b/experiments/exp2-dummy10/generate-env.sh @@ -21,6 +21,7 @@ for i in $(seq 0 9); do echo "FLEDGER_FLSIGNAL_HOST=${signalhost}" >"$envfile" echo "FLEDGER_SEND_MSG=${send_msg}" >>"$envfile" echo "FLEDGER_RECV_MSG=${recv_msg}" >>"$envfile" + echo "FLEDGER_NODE_NAME=${nodename}" >>"$envfile" echo "[node $nodename]" echo " <- ${recv_msg} [$i]" diff --git a/experiments/exp2-dummy10/model.py b/experiments/exp2-dummy10/model.py index 30d00620..fd04a997 100644 --- a/experiments/exp2-dummy10/model.py +++ b/experiments/exp2-dummy10/model.py @@ -8,7 +8,7 @@ def makeNode(i: int): sna = [makeNode(i) for i in range(10)] -link = net.connect(sna) +link = net.connect(sna, capacity==mbps(1), latency==ms(10)) for i in range(10): suffix = str(i + 1) diff --git a/experiments/exp2-dummy10/playbook.yaml b/experiments/exp2-dummy10/playbook.yaml index 6c4a1f57..810b5056 100644 --- a/experiments/exp2-dummy10/playbook.yaml +++ b/experiments/exp2-dummy10/playbook.yaml @@ -26,8 +26,7 @@ dest: /etc/systemd/system/ mode: preserve loop: - - fledger-send.service - - fledger-recv.service + - fledger.service - name: Remove config folders and logs ansible.builtin.file: @@ -35,8 +34,8 @@ state: absent loop: - /root/flnode - - /var/log/fledger-recv - - /var/log/fledger-send + - /var/log/fledger + - /var/log/flsignal - name: Run signaling server on node A hosts: a @@ -52,9 +51,8 @@ name: flsignal state: restarted daemon_reload: true - no_block: true -- name: Upload env and run fleger send / recv on each node +- name: Upload env and run fleger on each node hosts: all become: true tasks: @@ -63,19 +61,14 @@ src: "env.systemd/{{ inventory_hostname }}.env" dest: ~/env.systemd mode: preserve - - name: Start fledger node (send) + - name: Start fledger node ansible.builtin.systemd_service: - name: fledger-send + name: fledger state: restarted no_block: true daemon_reload: true - - name: Start fledger node (recv) - ansible.builtin.systemd_service: - name: fledger-recv - state: restarted - no_block: true - - name: Wait for message received on fledger node (recv) + - name: Wait for message received ansible.builtin.wait_for: - path: /var/log/fledger-recv + path: /var/log/fledger search_regex: "RECV_CHAT_MSG TRIGGERED" - timeout: 10 + timeout: 120 From e219cfab22b8129f4080737eebd05a09108e9019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 21:49:01 +0200 Subject: [PATCH 15/43] feat: exp3 working --- .../exp3-dummy10-router/fledger-recv.service | 14 ----- .../exp3-dummy10-router/fledger-send.service | 14 ----- .../exp3-dummy10-router/fledger.service | 16 +++++ .../exp3-dummy10-router/flsignal.service | 7 ++- .../exp3-dummy10-router/generate-env.sh | 9 ++- experiments/exp3-dummy10-router/model.py | 4 +- experiments/exp3-dummy10-router/playbook.yaml | 62 +++++++++++-------- 7 files changed, 65 insertions(+), 61 deletions(-) delete mode 100644 experiments/exp3-dummy10-router/fledger-recv.service delete mode 100644 experiments/exp3-dummy10-router/fledger-send.service create mode 100644 experiments/exp3-dummy10-router/fledger.service diff --git a/experiments/exp3-dummy10-router/fledger-recv.service b/experiments/exp3-dummy10-router/fledger-recv.service deleted file mode 100644 index f1285f12..00000000 --- a/experiments/exp3-dummy10-router/fledger-recv.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=oneshot -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_CENTRAL_HOST}:8765 --disable-turn-stun simulation recv-chat --print-new-messages ${FLEDGER_RECV_MSG} -RemainAfterExit=yes -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger-recv - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/fledger-send.service b/experiments/exp3-dummy10-router/fledger-send.service deleted file mode 100644 index f1457096..00000000 --- a/experiments/exp3-dummy10-router/fledger-send.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=oneshot -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_CENTRAL_HOST}:8765 --disable-turn-stun simulation send-chat ${FLEDGER_SEND_MSG} -RemainAfterExit=yes -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger-send - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/fledger.service b/experiments/exp3-dummy10-router/fledger.service new file mode 100644 index 00000000..96f6f555 --- /dev/null +++ b/experiments/exp3-dummy10-router/fledger.service @@ -0,0 +1,16 @@ + +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_CENTRAL_HOST}:8765 --name ${FLEDGER_NODE_NAME} --disable-turn-stun simulation --print-new-messages chat --send-msg ${FLEDGER_SEND_MSG} --recv-msg ${FLEDGER_RECV_MSG} +RemainAfterExit=yes +Restart=no +User=root +EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/flsignal.service b/experiments/exp3-dummy10-router/flsignal.service index 787b6bad..c1ea0035 100644 --- a/experiments/exp3-dummy10-router/flsignal.service +++ b/experiments/exp3-dummy10-router/flsignal.service @@ -3,10 +3,11 @@ Description=Fledger signaling server After=network.target [Service] -Type=oneshot -ExecStart=/root/flsignal -vvv -RemainAfterExit=yes +Type=simple +ExecStart=/root/flsignal -v User=root +Restart=no +StandardOutput=append:/var/log/flsignal [Install] WantedBy=multi-user.target diff --git a/experiments/exp3-dummy10-router/generate-env.sh b/experiments/exp3-dummy10-router/generate-env.sh index 9aa1a65e..54cd6680 100755 --- a/experiments/exp3-dummy10-router/generate-env.sh +++ b/experiments/exp3-dummy10-router/generate-env.sh @@ -25,9 +25,12 @@ for i in $(seq 0 $((amount - 1))); do signalhost="10.0.1.128" fi - echo "FLEDGER_CENTRAL_HOST=${signalhost}" >"$envfile" - echo "FLEDGER_SEND_MSG=${send_msg}" >>"$envfile" - echo "FLEDGER_RECV_MSG=${recv_msg}" >>"$envfile" + { + echo "FLEDGER_CENTRAL_HOST=${signalhost}" + echo "FLEDGER_SEND_MSG=${send_msg}" + echo "FLEDGER_RECV_MSG=${recv_msg}" + echo "FLEDGER_NODE_NAME=${nodename}" + } >"$envfile" echo "[node $nodename]" echo " <- ${recv_msg} [$i]" diff --git a/experiments/exp3-dummy10-router/model.py b/experiments/exp3-dummy10-router/model.py index 0e1508d5..4dbe1b85 100644 --- a/experiments/exp3-dummy10-router/model.py +++ b/experiments/exp3-dummy10-router/model.py @@ -15,8 +15,8 @@ def makeNode(i: int): sna.extend([router, central]) snb.extend([router, central]) -linka = net.connect(sna) -linkb = net.connect(snb) +linka = net.connect(sna, capacity==mbps(1), latency==ms(10)) +linkb = net.connect(snb, capacity==mbps(1), latency==ms(10)) linka[router].socket.addrs = ip4("10.0.0.1/24") linkb[router].socket.addrs = ip4("10.0.1.1/24") diff --git a/experiments/exp3-dummy10-router/playbook.yaml b/experiments/exp3-dummy10-router/playbook.yaml index 7260a90f..2b050be3 100644 --- a/experiments/exp3-dummy10-router/playbook.yaml +++ b/experiments/exp3-dummy10-router/playbook.yaml @@ -1,12 +1,17 @@ -- name: Generate environments on ansible host +- name: Prepare ansible host hosts: 127.0.0.1 connection: local tasks: - - name: Generate environemnts + - name: Generate node environments changed_when: true ansible.builtin.command: cmd: ./generate-env.sh + - name: Create metrics directory + ansible.builtin.file: + path: metrics + state: directory + - name: Clean nodes and upload files (binaries and services) hosts: nodes become: true @@ -19,12 +24,9 @@ - name: Copy fledger services ansible.builtin.copy: - src: "{{ item }}" + src: fledger.service dest: /etc/systemd/system/ mode: preserve - loop: - - fledger-send.service - - fledger-recv.service - name: Remove config folders and log files ansible.builtin.file: @@ -32,8 +34,8 @@ state: absent loop: - /root/flnode - - /var/log/fledger-recv - - /var/log/fledger-send + - /var/log/fledger + - /var/log/flsignal - name: Run signaling server on central node hosts: central @@ -54,7 +56,6 @@ name: flsignal state: restarted daemon_reload: true - no_block: true - name: Disable ip forwarding ansible.posix.sysctl: name: net.ipv4.ip_forward @@ -63,7 +64,7 @@ state: present reload: true -- name: Upload env and run fleger send / recv on each node +- name: Upload env and run fleger on each node hosts: nodes become: true tasks: @@ -72,23 +73,34 @@ src: "env.systemd/{{ inventory_hostname }}.env" dest: ~/env.systemd mode: preserve - - name: Stop fledger node (recv) - ansible.builtin.systemd_service: - name: fledger-recv - state: stopped - daemon_reload: true - - name: Start fledger node (recv) - ansible.builtin.systemd_service: - name: fledger-recv - state: restarted - no_block: true - - name: Start fledger node (send) + - name: (Re)start fledger node ansible.builtin.systemd_service: - name: fledger-send + name: fledger state: restarted no_block: true - - name: Wait for message received on fledger node (recv) + daemon_reload: true + - name: Wait for message received on fledger node ansible.builtin.wait_for: - path: /var/log/fledger-recv + path: /var/log/fledger search_regex: "RECV_CHAT_MSG TRIGGERED" - timeout: 600 + timeout: 120 + +- name: Download metrics + hosts: nodes + become: true + tasks: + - name: Download metrics + ansible.builtin.fetch: + src: /tmp/out.metrics + flat: true + dest: "metrics/{{ inventory_hostname }}.metrics" + +- name: Assemble metrics into one file + hosts: 127.0.0.1 + connection: local + tasks: + - name: Assemble metrics + ansible.builtin.assemble: + src: metrics + dest: assembled.metrics + mode: "644" From bdb8e1693c4bf919817c921b804a4ff22e69f90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 31 Mar 2025 21:53:21 +0200 Subject: [PATCH 16/43] refactor: rename dummy experiments to onechat --- experiments/{exp1-dummy => exp1-onechat2}/README.md | 0 .../{exp1-dummy => exp1-onechat2}/explanation.png | Bin .../{exp1-dummy => exp1-onechat2}/explanation.puml | 0 .../fledger-recv.service | 0 .../fledger-send.service | 0 .../{exp1-dummy => exp1-onechat2}/flsignal.service | 0 experiments/{exp1-dummy => exp1-onechat2}/hosts | 0 experiments/{exp1-dummy => exp1-onechat2}/model.py | 0 .../{exp1-dummy => exp1-onechat2}/playbook.yaml | 0 .../{exp2-dummy10 => exp2-onechat10}/README.md | 0 .../explanation.png | Bin .../explanation.puml | 0 .../fledger.service | 0 .../flsignal.service | 0 .../generate-env.sh | 0 experiments/{exp2-dummy10 => exp2-onechat10}/hosts | 0 .../{exp2-dummy10 => exp2-onechat10}/model.py | 0 .../{exp2-dummy10 => exp2-onechat10}/playbook.yaml | 0 .../README.md | 0 .../fledger.service | 0 .../flsignal.service | 0 .../generate-env.sh | 0 .../{exp3-dummy10-router => exp3-onechat2x5}/hosts | 0 .../model.py | 0 .../network.png | Bin .../network.puml | 0 .../playbook.yaml | 0 27 files changed, 0 insertions(+), 0 deletions(-) rename experiments/{exp1-dummy => exp1-onechat2}/README.md (100%) rename experiments/{exp1-dummy => exp1-onechat2}/explanation.png (100%) rename experiments/{exp1-dummy => exp1-onechat2}/explanation.puml (100%) rename experiments/{exp1-dummy => exp1-onechat2}/fledger-recv.service (100%) rename experiments/{exp1-dummy => exp1-onechat2}/fledger-send.service (100%) rename experiments/{exp1-dummy => exp1-onechat2}/flsignal.service (100%) rename experiments/{exp1-dummy => exp1-onechat2}/hosts (100%) rename experiments/{exp1-dummy => exp1-onechat2}/model.py (100%) rename experiments/{exp1-dummy => exp1-onechat2}/playbook.yaml (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/README.md (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/explanation.png (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/explanation.puml (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/fledger.service (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/flsignal.service (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/generate-env.sh (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/hosts (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/model.py (100%) rename experiments/{exp2-dummy10 => exp2-onechat10}/playbook.yaml (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/README.md (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/fledger.service (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/flsignal.service (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/generate-env.sh (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/hosts (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/model.py (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/network.png (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/network.puml (100%) rename experiments/{exp3-dummy10-router => exp3-onechat2x5}/playbook.yaml (100%) diff --git a/experiments/exp1-dummy/README.md b/experiments/exp1-onechat2/README.md similarity index 100% rename from experiments/exp1-dummy/README.md rename to experiments/exp1-onechat2/README.md diff --git a/experiments/exp1-dummy/explanation.png b/experiments/exp1-onechat2/explanation.png similarity index 100% rename from experiments/exp1-dummy/explanation.png rename to experiments/exp1-onechat2/explanation.png diff --git a/experiments/exp1-dummy/explanation.puml b/experiments/exp1-onechat2/explanation.puml similarity index 100% rename from experiments/exp1-dummy/explanation.puml rename to experiments/exp1-onechat2/explanation.puml diff --git a/experiments/exp1-dummy/fledger-recv.service b/experiments/exp1-onechat2/fledger-recv.service similarity index 100% rename from experiments/exp1-dummy/fledger-recv.service rename to experiments/exp1-onechat2/fledger-recv.service diff --git a/experiments/exp1-dummy/fledger-send.service b/experiments/exp1-onechat2/fledger-send.service similarity index 100% rename from experiments/exp1-dummy/fledger-send.service rename to experiments/exp1-onechat2/fledger-send.service diff --git a/experiments/exp1-dummy/flsignal.service b/experiments/exp1-onechat2/flsignal.service similarity index 100% rename from experiments/exp1-dummy/flsignal.service rename to experiments/exp1-onechat2/flsignal.service diff --git a/experiments/exp1-dummy/hosts b/experiments/exp1-onechat2/hosts similarity index 100% rename from experiments/exp1-dummy/hosts rename to experiments/exp1-onechat2/hosts diff --git a/experiments/exp1-dummy/model.py b/experiments/exp1-onechat2/model.py similarity index 100% rename from experiments/exp1-dummy/model.py rename to experiments/exp1-onechat2/model.py diff --git a/experiments/exp1-dummy/playbook.yaml b/experiments/exp1-onechat2/playbook.yaml similarity index 100% rename from experiments/exp1-dummy/playbook.yaml rename to experiments/exp1-onechat2/playbook.yaml diff --git a/experiments/exp2-dummy10/README.md b/experiments/exp2-onechat10/README.md similarity index 100% rename from experiments/exp2-dummy10/README.md rename to experiments/exp2-onechat10/README.md diff --git a/experiments/exp2-dummy10/explanation.png b/experiments/exp2-onechat10/explanation.png similarity index 100% rename from experiments/exp2-dummy10/explanation.png rename to experiments/exp2-onechat10/explanation.png diff --git a/experiments/exp2-dummy10/explanation.puml b/experiments/exp2-onechat10/explanation.puml similarity index 100% rename from experiments/exp2-dummy10/explanation.puml rename to experiments/exp2-onechat10/explanation.puml diff --git a/experiments/exp2-dummy10/fledger.service b/experiments/exp2-onechat10/fledger.service similarity index 100% rename from experiments/exp2-dummy10/fledger.service rename to experiments/exp2-onechat10/fledger.service diff --git a/experiments/exp2-dummy10/flsignal.service b/experiments/exp2-onechat10/flsignal.service similarity index 100% rename from experiments/exp2-dummy10/flsignal.service rename to experiments/exp2-onechat10/flsignal.service diff --git a/experiments/exp2-dummy10/generate-env.sh b/experiments/exp2-onechat10/generate-env.sh similarity index 100% rename from experiments/exp2-dummy10/generate-env.sh rename to experiments/exp2-onechat10/generate-env.sh diff --git a/experiments/exp2-dummy10/hosts b/experiments/exp2-onechat10/hosts similarity index 100% rename from experiments/exp2-dummy10/hosts rename to experiments/exp2-onechat10/hosts diff --git a/experiments/exp2-dummy10/model.py b/experiments/exp2-onechat10/model.py similarity index 100% rename from experiments/exp2-dummy10/model.py rename to experiments/exp2-onechat10/model.py diff --git a/experiments/exp2-dummy10/playbook.yaml b/experiments/exp2-onechat10/playbook.yaml similarity index 100% rename from experiments/exp2-dummy10/playbook.yaml rename to experiments/exp2-onechat10/playbook.yaml diff --git a/experiments/exp3-dummy10-router/README.md b/experiments/exp3-onechat2x5/README.md similarity index 100% rename from experiments/exp3-dummy10-router/README.md rename to experiments/exp3-onechat2x5/README.md diff --git a/experiments/exp3-dummy10-router/fledger.service b/experiments/exp3-onechat2x5/fledger.service similarity index 100% rename from experiments/exp3-dummy10-router/fledger.service rename to experiments/exp3-onechat2x5/fledger.service diff --git a/experiments/exp3-dummy10-router/flsignal.service b/experiments/exp3-onechat2x5/flsignal.service similarity index 100% rename from experiments/exp3-dummy10-router/flsignal.service rename to experiments/exp3-onechat2x5/flsignal.service diff --git a/experiments/exp3-dummy10-router/generate-env.sh b/experiments/exp3-onechat2x5/generate-env.sh similarity index 100% rename from experiments/exp3-dummy10-router/generate-env.sh rename to experiments/exp3-onechat2x5/generate-env.sh diff --git a/experiments/exp3-dummy10-router/hosts b/experiments/exp3-onechat2x5/hosts similarity index 100% rename from experiments/exp3-dummy10-router/hosts rename to experiments/exp3-onechat2x5/hosts diff --git a/experiments/exp3-dummy10-router/model.py b/experiments/exp3-onechat2x5/model.py similarity index 100% rename from experiments/exp3-dummy10-router/model.py rename to experiments/exp3-onechat2x5/model.py diff --git a/experiments/exp3-dummy10-router/network.png b/experiments/exp3-onechat2x5/network.png similarity index 100% rename from experiments/exp3-dummy10-router/network.png rename to experiments/exp3-onechat2x5/network.png diff --git a/experiments/exp3-dummy10-router/network.puml b/experiments/exp3-onechat2x5/network.puml similarity index 100% rename from experiments/exp3-dummy10-router/network.puml rename to experiments/exp3-onechat2x5/network.puml diff --git a/experiments/exp3-dummy10-router/playbook.yaml b/experiments/exp3-onechat2x5/playbook.yaml similarity index 100% rename from experiments/exp3-dummy10-router/playbook.yaml rename to experiments/exp3-onechat2x5/playbook.yaml From 1e82f44c59e7ee2f9881b25d45083f3d4b7af9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Tue, 1 Apr 2025 12:18:48 +0200 Subject: [PATCH 17/43] feat: bootwait time as cli argument --- cli/fledger/src/main.rs | 14 ++++++++------ cli/fledger/src/simulation.rs | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 8173d4bd..8e27f393 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -82,10 +82,12 @@ pub struct Args { #[arg(long, default_value = "false")] log_dht_storage: bool, - /// Do not wait for a random amount - /// of ms if the command is simulation - #[arg(long, default_value = "false")] - no_simulation_wait: bool, + /// Wait a random amount of ms, bounded by + /// bootwait_max, before starting the node. + /// This allows the signaling server to be + /// less overwhelmed + #[arg(long, default_value = "0")] + bootwait_max: u64, #[command(subcommand)] command: Option, @@ -141,11 +143,11 @@ async fn main() -> anyhow::Result<()> { // wait a random amount of time before running a simulation // to avoid overloading the signaling server - if !args.no_simulation_wait { + if !args.bootwait_max != 0 { match args.command.clone() { Some(cmd) => match cmd { Commands::Simulation(_) => { - let randtime: u64 = random::() % 5000; + let randtime: u64 = random::() % args.bootwait_max; log::info!("Waiting {}ms before running this node...", randtime); wait_ms(randtime).await; } diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 4c2a264d..c6493f90 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -67,10 +67,12 @@ impl SimulationHandler { wait_ms(1000).await; let fledger_message_total = f.node.gossip.as_ref().unwrap().chat_events().len(); + let fledger_connected_total = f.node.nodes_connected()?.len(); absolute_counter!( "fledger_message_total", fledger_message_total.try_into().unwrap() ); + absolute_counter!("fledger_connected_total", fledger_connected_total as u64); increment_counter!("fledger_iterations_total"); if simulation_args.print_new_messages { From d7dc29cd7ffa78eef34f01b556c24462969aad46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Tue, 1 Apr 2025 12:40:39 +0200 Subject: [PATCH 18/43] feat: exp4 onechat 2x25 --- .gitignore | 1 + experiments/README.md | 15 ++ experiments/exp4-onechat2x25/README.md | 4 + experiments/exp4-onechat2x25/fledger.service | 27 ++++ experiments/exp4-onechat2x25/flsignal.service | 13 ++ experiments/exp4-onechat2x25/generate-env.sh | 38 ++++++ experiments/exp4-onechat2x25/hosts | 54 ++++++++ experiments/exp4-onechat2x25/model.py | 32 +++++ experiments/exp4-onechat2x25/network.png | Bin 0 -> 11060 bytes experiments/exp4-onechat2x25/network.puml | 28 ++++ experiments/exp4-onechat2x25/playbook.yaml | 128 ++++++++++++++++++ 11 files changed, 340 insertions(+) create mode 100644 experiments/exp4-onechat2x25/README.md create mode 100644 experiments/exp4-onechat2x25/fledger.service create mode 100644 experiments/exp4-onechat2x25/flsignal.service create mode 100755 experiments/exp4-onechat2x25/generate-env.sh create mode 100644 experiments/exp4-onechat2x25/hosts create mode 100644 experiments/exp4-onechat2x25/model.py create mode 100644 experiments/exp4-onechat2x25/network.png create mode 100644 experiments/exp4-onechat2x25/network.puml create mode 100644 experiments/exp4-onechat2x25/playbook.yaml diff --git a/.gitignore b/.gitignore index e1c46044..a60361fd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ target-common/ femme dist/ *.code-workspace +assembled.metrics diff --git a/experiments/README.md b/experiments/README.md index 5c04d278..2791b610 100644 --- a/experiments/README.md +++ b/experiments/README.md @@ -22,6 +22,21 @@ The services can either be ran synchronously (ansible blocks) or asynchronously Check `exp1-dummy` for an example of both (fledger-send is blocking and fledger-recv is non-blocking). +## Create hosts file for a new experiment + +When creating a new experiment, there is a simple (but imperfect) command to generate the hosts file. +It's `mrg nodes generate inventory expX.fledger.abehsser > hosts` + +- abehsser is the project name +- fledger the experiment name +- expX the reservation name). + +You'll need to modify it to: + +1. Group the nodes together in a \[nodes\] group. +2. Put the central server in a \[central\] group. +3. Remove the router. + ## Running experiments ### Setup mergeTB diff --git a/experiments/exp4-onechat2x25/README.md b/experiments/exp4-onechat2x25/README.md new file mode 100644 index 00000000..99343a9e --- /dev/null +++ b/experiments/exp4-onechat2x25/README.md @@ -0,0 +1,4 @@ +# Exp3 dummy2x25 + +Two LANs of 25 nodes separated by a router. +Single central server for signaling diff --git a/experiments/exp4-onechat2x25/fledger.service b/experiments/exp4-onechat2x25/fledger.service new file mode 100644 index 00000000..a160dcac --- /dev/null +++ b/experiments/exp4-onechat2x25/fledger.service @@ -0,0 +1,27 @@ + +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger \ + --config /root/flnode \ + --name ${FLEDGER_NODE_NAME} \ + --disable-turn-stun \ + --bootwait-max 15000 \ + --signal-url ws://${FLEDGER_CENTRAL_HOST}:8765 \ + -v \ + simulation \ + --print-new-messages \ + chat \ + --send-msg ${FLEDGER_SEND_MSG} \ + --recv-msg ${FLEDGER_RECV_MSG} +RemainAfterExit=yes +Restart=no +User=root +EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp4-onechat2x25/flsignal.service b/experiments/exp4-onechat2x25/flsignal.service new file mode 100644 index 00000000..c1ea0035 --- /dev/null +++ b/experiments/exp4-onechat2x25/flsignal.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger signaling server +After=network.target + +[Service] +Type=simple +ExecStart=/root/flsignal -v +User=root +Restart=no +StandardOutput=append:/var/log/flsignal + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp4-onechat2x25/generate-env.sh b/experiments/exp4-onechat2x25/generate-env.sh new file mode 100755 index 00000000..3cb3c8a9 --- /dev/null +++ b/experiments/exp4-onechat2x25/generate-env.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +mkdir -p env.systemd + +#amount=100 +amount=50 + +messages=() + +for i in $(seq $amount); do + message="$(openssl rand -hex 16)" + messages+=("$message") +done + +for i in $(seq 0 $((amount - 1))); do + j=$((("$i" + 2) % amount)) + nodename="n${i}" + envfile="env.systemd/${nodename}.env" + send_msg="${messages[$j]}" + recv_msg="${messages[$i]}" + + if test "$i" -lt 25; then + signalhost="10.0.0.128" + else + signalhost="10.0.1.128" + fi + + { + echo "FLEDGER_CENTRAL_HOST=${signalhost}" + echo "FLEDGER_SEND_MSG=${send_msg}" + echo "FLEDGER_RECV_MSG=${recv_msg}" + echo "FLEDGER_NODE_NAME=${nodename}" + } >"$envfile" + + echo "[node $nodename]" + echo " <- ${recv_msg} [$i]" + echo " -> ${send_msg} [$j]" +done diff --git a/experiments/exp4-onechat2x25/hosts b/experiments/exp4-onechat2x25/hosts new file mode 100644 index 00000000..aaf34ab2 --- /dev/null +++ b/experiments/exp4-onechat2x25/hosts @@ -0,0 +1,54 @@ +[nodes] +n0 +n1 +n2 +n3 +n4 +n5 +n6 +n7 +n8 +n9 +n10 +n11 +n12 +n13 +n14 +n15 +n16 +n17 +n18 +n19 +n20 +n21 +n22 +n23 +n24 +n25 +n26 +n27 +n28 +n29 +n30 +n31 +n32 +n33 +n34 +n35 +n36 +n37 +n38 +n39 +n40 +n41 +n42 +n43 +n44 +n45 +n46 +n47 +n48 +n49 + +[central] +central diff --git a/experiments/exp4-onechat2x25/model.py b/experiments/exp4-onechat2x25/model.py new file mode 100644 index 00000000..c8d708f9 --- /dev/null +++ b/experiments/exp4-onechat2x25/model.py @@ -0,0 +1,32 @@ +from mergexp import * + +net = Network('exp4', routing == static) + +def makeNode(i: int): + name = f"n{i}" + return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) + +sna = [makeNode(i) for i in range(25)] +snb = [makeNode(i) for i in range(25, 50)] + +router = net.node('router', proc.cores>=1, memory.capacity>=mb(512)) +central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) + +sna.extend([router, central]) +snb.extend([router, central]) + +linka = net.connect(sna, capacity==mbps(100), latency==ms(10)) +linkb = net.connect(snb, capacity==mbps(100), latency==ms(10)) + +linka[router].socket.addrs = ip4("10.0.0.1/24") +linkb[router].socket.addrs = ip4("10.0.1.1/24") + +linka[central].socket.addrs = ip4("10.0.0.128/24") +linkb[central].socket.addrs = ip4("10.0.1.128/24") + +for i in range(25): + suffix = str(i + 2) + linka[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") + linkb[snb[i]].socket.addrs = ip4(f"10.0.1.{suffix}/24") + +experiment(net) diff --git a/experiments/exp4-onechat2x25/network.png b/experiments/exp4-onechat2x25/network.png new file mode 100644 index 0000000000000000000000000000000000000000..08907d0f054e50195dc9639abc79b0be1f3d7d01 GIT binary patch literal 11060 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 ThM9pE8X+n-H5Ciw&7b@iJ4Z{B literal 0 HcmV?d00001 diff --git a/experiments/exp4-onechat2x25/network.puml b/experiments/exp4-onechat2x25/network.puml new file mode 100644 index 00000000..b171c2a5 --- /dev/null +++ b/experiments/exp4-onechat2x25/network.puml @@ -0,0 +1,28 @@ +@startuml +nwdiag { + network sna { + width = full + address = 10.0.0.0/24 + + n0 [address = 10.0.0.2]; + nx_a [shape = collections, description = n1...n23]; + n24 [address = 10.0.0.26]; + + router [address = 10.0.0.1]; + central [address = 10.0.0.128]; + } + + network snb { + address = 10.0.1.0/24 + + n25 [address = 10.0.1.2]; + nx_b [shape = collections, description = n26...n48]; + n49 [address = 10.0.1.26]; + + router [address = 10.0.1.1]; + central [address = 10.0.1.128]; + } +} +@enduml + + diff --git a/experiments/exp4-onechat2x25/playbook.yaml b/experiments/exp4-onechat2x25/playbook.yaml new file mode 100644 index 00000000..ebd123f2 --- /dev/null +++ b/experiments/exp4-onechat2x25/playbook.yaml @@ -0,0 +1,128 @@ +- name: Prepare ansible host + hosts: 127.0.0.1 + connection: local + tasks: + - name: Generate node environments + changed_when: true + ansible.builtin.command: + cmd: ./generate-env.sh + + - name: Create metrics directory + ansible.builtin.file: + path: metrics + state: directory + mode: "0755" + +- name: Prepare fledger nodes + hosts: nodes + become: true + tasks: + - name: Copy binary + ansible.builtin.copy: + src: ~/fledger + dest: ~/ + mode: preserve + + - name: Copy service + ansible.builtin.copy: + src: fledger.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Stop service + ansible.builtin.systemd_service: + name: fledger + state: stopped + daemon_reload: true + + - name: Copy environment + ansible.builtin.copy: + src: "env.systemd/{{ inventory_hostname }}.env" + dest: ~/env.systemd + mode: preserve + + - name: Remove old node and logs + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /root/flnode + - /var/log/fledger + + +- name: Prepare central node + hosts: central + become: true + tasks: + - name: Disable ip forwarding + ansible.posix.sysctl: + name: net.ipv4.ip_forward + value: 0 + sysctl_set: true + state: present + reload: true + + - name: Copy flsignal binary + ansible.builtin.copy: + src: ~/flsignal + dest: ~/ + mode: preserve + + - name: Copy flsignal service + ansible.builtin.copy: + src: flsignal.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Stop flsignal + ansible.builtin.systemd_service: + name: flsignal + state: stopped + daemon_reload: true + + - name: Remove flsignal logs + ansible.builtin.file: + path: /var/log/flsignal + state: absent + + - name: Start flsignal + ansible.builtin.systemd_service: + name: flsignal + state: restarted + +- name: Run the simulation + hosts: nodes + become: true + timeout: 300 + tasks: + - name: Start fledger + ansible.builtin.systemd_service: + name: fledger + state: restarted + daemon_reload: true + + - name: Wait for success trigger + ansible.builtin.wait_for: + path: /var/log/fledger + search_regex: "RECV_CHAT_MSG TRIGGERED" + +# Must remain separate so it runs even if the simulation fails +- name: Download metrics + hosts: nodes + become: true + tasks: + - name: Download metrics + ansible.builtin.fetch: + src: /tmp/out.metrics + flat: true + dest: "metrics/{{ inventory_hostname }}.metrics" + +- name: Assemble metrics into one file + hosts: 127.0.0.1 + connection: local + tasks: + - name: Assemble metrics + ansible.builtin.assemble: + src: metrics + dest: assembled.metrics + mode: "644" From d3b5feb5fca67ef2ae9437720b73a9f8a2e6da6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sat, 12 Apr 2025 21:10:57 +0200 Subject: [PATCH 19/43] feat: exp5 onechat 4x25 (not working) --- experiments/exp5-onechat4x25/README.md | 9 ++ experiments/exp5-onechat4x25/fledger.service | 26 ++++ experiments/exp5-onechat4x25/flsignal.service | 13 ++ experiments/exp5-onechat4x25/generate-env.sh | 43 ++++++ experiments/exp5-onechat4x25/hosts | 104 ++++++++++++++ experiments/exp5-onechat4x25/model.py | 56 ++++++++ experiments/exp5-onechat4x25/network.png | Bin 0 -> 26144 bytes experiments/exp5-onechat4x25/network.puml | 52 +++++++ experiments/exp5-onechat4x25/playbook.yaml | 128 ++++++++++++++++++ 9 files changed, 431 insertions(+) create mode 100644 experiments/exp5-onechat4x25/README.md create mode 100644 experiments/exp5-onechat4x25/fledger.service create mode 100644 experiments/exp5-onechat4x25/flsignal.service create mode 100755 experiments/exp5-onechat4x25/generate-env.sh create mode 100644 experiments/exp5-onechat4x25/hosts create mode 100644 experiments/exp5-onechat4x25/model.py create mode 100644 experiments/exp5-onechat4x25/network.png create mode 100644 experiments/exp5-onechat4x25/network.puml create mode 100644 experiments/exp5-onechat4x25/playbook.yaml diff --git a/experiments/exp5-onechat4x25/README.md b/experiments/exp5-onechat4x25/README.md new file mode 100644 index 00000000..5e970691 --- /dev/null +++ b/experiments/exp5-onechat4x25/README.md @@ -0,0 +1,9 @@ +# Exp5 onechat4x25 + +4 LANs of 25 nodes. + +LANs are organized in a square, i.e. there are four routers total +that link 2 lans each. +See plantuml diagram / network.png. + +Single central server for signaling diff --git a/experiments/exp5-onechat4x25/fledger.service b/experiments/exp5-onechat4x25/fledger.service new file mode 100644 index 00000000..18c75c58 --- /dev/null +++ b/experiments/exp5-onechat4x25/fledger.service @@ -0,0 +1,26 @@ +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger \ + --config /root/flnode \ + --name ${FLEDGER_NODE_NAME} \ + --disable-turn-stun \ + --bootwait-max 60000 \ + --signal-url ws://${FLEDGER_CENTRAL_HOST}:8765 \ + -v \ + simulation \ + --print-new-messages \ + chat \ + --send-msg ${FLEDGER_SEND_MSG} \ + --recv-msg ${FLEDGER_RECV_MSG} +RemainAfterExit=yes +Restart=no +User=root +EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp5-onechat4x25/flsignal.service b/experiments/exp5-onechat4x25/flsignal.service new file mode 100644 index 00000000..c1ea0035 --- /dev/null +++ b/experiments/exp5-onechat4x25/flsignal.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger signaling server +After=network.target + +[Service] +Type=simple +ExecStart=/root/flsignal -v +User=root +Restart=no +StandardOutput=append:/var/log/flsignal + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp5-onechat4x25/generate-env.sh b/experiments/exp5-onechat4x25/generate-env.sh new file mode 100755 index 00000000..5ef7e3ab --- /dev/null +++ b/experiments/exp5-onechat4x25/generate-env.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +mkdir -p env.systemd + +amount=100 + +messages=() + +for i in $(seq $amount); do + message="$(openssl rand -hex 16)" + messages+=("$message") +done + +for i in $(seq 0 $((amount - 1))); do + j=$((("$i" + 2) % amount)) + nodename="n${i}" + envfile="env.systemd/${nodename}.env" + send_msg="${messages[$j]}" + recv_msg="${messages[$i]}" + + if test "$i" -lt 25; then + signalhost="10.0.0.128" + elif test "$i" -lt 50; then + signalhost="10.0.1.128" + elif test "$i" -lt 75; then + signalhost="10.0.2.128" + else + signalhost="10.0.3.128" + fi + + signalhost="10.0.128.128" + + { + echo "FLEDGER_CENTRAL_HOST=${signalhost}" + echo "FLEDGER_SEND_MSG=${send_msg}" + echo "FLEDGER_RECV_MSG=${recv_msg}" + echo "FLEDGER_NODE_NAME=${nodename}" + } >"$envfile" + + echo "[node $nodename]" + echo " <- ${recv_msg} [$i]" + echo " -> ${send_msg} [$j]" +done diff --git a/experiments/exp5-onechat4x25/hosts b/experiments/exp5-onechat4x25/hosts new file mode 100644 index 00000000..685648cd --- /dev/null +++ b/experiments/exp5-onechat4x25/hosts @@ -0,0 +1,104 @@ +[nodes] +n0 +n1 +n2 +n3 +n4 +n5 +n6 +n7 +n8 +n9 +n10 +n11 +n12 +n13 +n14 +n15 +n16 +n17 +n18 +n19 +n20 +n21 +n22 +n23 +n24 +n25 +n26 +n27 +n28 +n29 +n30 +n31 +n32 +n33 +n34 +n35 +n36 +n37 +n38 +n39 +n40 +n41 +n42 +n43 +n44 +n45 +n46 +n47 +n48 +n49 +n50 +n51 +n52 +n53 +n54 +n55 +n56 +n57 +n58 +n59 +n60 +n61 +n62 +n63 +n64 +n65 +n66 +n67 +n68 +n69 +n70 +n71 +n72 +n73 +n74 +n75 +n76 +n77 +n78 +n79 +n80 +n81 +n82 +n83 +n84 +n85 +n86 +n87 +n88 +n89 +n90 +n91 +n92 +n93 +n94 +n95 +n96 +n97 +n98 +n99 + +[central] +central diff --git a/experiments/exp5-onechat4x25/model.py b/experiments/exp5-onechat4x25/model.py new file mode 100644 index 00000000..bfadfade --- /dev/null +++ b/experiments/exp5-onechat4x25/model.py @@ -0,0 +1,56 @@ +from mergexp import * + +net = Network('exp5', routing == static) + +def makeNode(i: int): + name = f"n{i}" + return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) + +lans = [ + [makeNode(i) for i in range(25)], + [makeNode(i) for i in range(25, 50)], + [makeNode(i) for i in range(50, 75)], + [makeNode(i) for i in range(75, 100)] +] + +router01 = net.node('router01', proc.cores>=1, memory.capacity>=mb(512)) +router12 = net.node('router12', proc.cores>=1, memory.capacity>=mb(512)) +router23 = net.node('router23', proc.cores>=1, memory.capacity>=mb(512)) + +central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) + +lans[0].extend([router01]) +lans[1].extend([router01, router12]) +lans[2].extend([router12, router23]) +lans[3].extend([router23]) + +links = [ + net.connect(lans[i], capacity==mbps(100), latency==ms(10)) + for i in range(len(lans)) +] + + +links[0][router01].socket.addrs = ip4(f"10.0.0.2/24") + +links[1][router01].socket.addrs = ip4(f"10.0.1.1/24") +links[1][router12].socket.addrs = ip4(f"10.0.1.2/24") + +links[2][router12].socket.addrs = ip4(f"10.0.2.1/24") +links[2][router23].socket.addrs = ip4(f"10.0.2.2/24") + +links[3][router23].socket.addrs = ip4(f"10.0.3.1/24") + +centralLan = [router12, central] +centralLink = net.connect(centralLan, capacity==mbps(100), latency==ms(10)) + +centralLink[central].socket.addrs = ip4(f"10.0.128.128/24") +centralLink[router12].socket.addrs = ip4(f"10.0.128.1/24") + +for i in range(len(links)): + lan = lans[i] + link = links[i] + for j in range(25): + n = str(j + 10) + link[lan[j]].socket.addrs = ip4(f"10.0.{i}.{n}/24") + +experiment(net) diff --git a/experiments/exp5-onechat4x25/network.png b/experiments/exp5-onechat4x25/network.png new file mode 100644 index 0000000000000000000000000000000000000000..b677c2fe0569cbe236bab146828b0d37e6b69724 GIT binary patch literal 26144 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 QfSpJNPgg&ebxsLQ02nyT Date: Mon, 14 Apr 2025 16:56:08 +0200 Subject: [PATCH 20/43] chore: rename experiments --- .../{exp1-onechat2 => exp01-onechat2}/README.md | 0 .../explanation.png | Bin .../explanation.puml | 0 .../fledger-recv.service | 0 .../fledger-send.service | 0 .../flsignal.service | 0 experiments/{exp1-onechat2 => exp01-onechat2}/hosts | 0 .../{exp1-onechat2 => exp01-onechat2}/model.py | 0 .../{exp1-onechat2 => exp01-onechat2}/playbook.yaml | 0 .../{exp2-onechat10 => exp02-onechat10}/README.md | 0 .../explanation.png | Bin .../explanation.puml | 0 .../fledger.service | 0 .../flsignal.service | 0 .../generate-env.sh | 0 .../{exp2-onechat10 => exp02-onechat10}/hosts | 0 .../{exp2-onechat10 => exp02-onechat10}/model.py | 0 .../playbook.yaml | 0 .../{exp3-onechat2x5 => exp03-onechat2x5}/README.md | 0 .../fledger.service | 0 .../flsignal.service | 0 .../generate-env.sh | 0 .../{exp3-onechat2x5 => exp03-onechat2x5}/hosts | 0 .../{exp3-onechat2x5 => exp03-onechat2x5}/model.py | 0 .../network.png | Bin .../network.puml | 0 .../playbook.yaml | 0 .../README.md | 0 .../fledger.service | 0 .../flsignal.service | 0 .../generate-env.sh | 0 .../{exp4-onechat2x25 => exp04-onechat2x25}/hosts | 0 .../model.py | 0 .../network.png | Bin .../network.puml | 0 .../playbook.yaml | 0 .../README.md | 0 .../fledger.service | 0 .../flsignal.service | 0 .../generate-env.sh | 0 .../{exp5-onechat4x25 => exp05-onechat4x25}/hosts | 0 .../model.py | 0 .../network.png | Bin .../network.puml | 0 .../playbook.yaml | 0 45 files changed, 0 insertions(+), 0 deletions(-) rename experiments/{exp1-onechat2 => exp01-onechat2}/README.md (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/explanation.png (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/explanation.puml (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/fledger-recv.service (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/fledger-send.service (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/flsignal.service (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/hosts (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/model.py (100%) rename experiments/{exp1-onechat2 => exp01-onechat2}/playbook.yaml (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/README.md (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/explanation.png (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/explanation.puml (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/fledger.service (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/flsignal.service (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/generate-env.sh (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/hosts (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/model.py (100%) rename experiments/{exp2-onechat10 => exp02-onechat10}/playbook.yaml (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/README.md (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/fledger.service (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/flsignal.service (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/generate-env.sh (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/hosts (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/model.py (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/network.png (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/network.puml (100%) rename experiments/{exp3-onechat2x5 => exp03-onechat2x5}/playbook.yaml (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/README.md (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/fledger.service (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/flsignal.service (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/generate-env.sh (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/hosts (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/model.py (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/network.png (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/network.puml (100%) rename experiments/{exp4-onechat2x25 => exp04-onechat2x25}/playbook.yaml (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/README.md (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/fledger.service (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/flsignal.service (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/generate-env.sh (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/hosts (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/model.py (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/network.png (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/network.puml (100%) rename experiments/{exp5-onechat4x25 => exp05-onechat4x25}/playbook.yaml (100%) diff --git a/experiments/exp1-onechat2/README.md b/experiments/exp01-onechat2/README.md similarity index 100% rename from experiments/exp1-onechat2/README.md rename to experiments/exp01-onechat2/README.md diff --git a/experiments/exp1-onechat2/explanation.png b/experiments/exp01-onechat2/explanation.png similarity index 100% rename from experiments/exp1-onechat2/explanation.png rename to experiments/exp01-onechat2/explanation.png diff --git a/experiments/exp1-onechat2/explanation.puml b/experiments/exp01-onechat2/explanation.puml similarity index 100% rename from experiments/exp1-onechat2/explanation.puml rename to experiments/exp01-onechat2/explanation.puml diff --git a/experiments/exp1-onechat2/fledger-recv.service b/experiments/exp01-onechat2/fledger-recv.service similarity index 100% rename from experiments/exp1-onechat2/fledger-recv.service rename to experiments/exp01-onechat2/fledger-recv.service diff --git a/experiments/exp1-onechat2/fledger-send.service b/experiments/exp01-onechat2/fledger-send.service similarity index 100% rename from experiments/exp1-onechat2/fledger-send.service rename to experiments/exp01-onechat2/fledger-send.service diff --git a/experiments/exp1-onechat2/flsignal.service b/experiments/exp01-onechat2/flsignal.service similarity index 100% rename from experiments/exp1-onechat2/flsignal.service rename to experiments/exp01-onechat2/flsignal.service diff --git a/experiments/exp1-onechat2/hosts b/experiments/exp01-onechat2/hosts similarity index 100% rename from experiments/exp1-onechat2/hosts rename to experiments/exp01-onechat2/hosts diff --git a/experiments/exp1-onechat2/model.py b/experiments/exp01-onechat2/model.py similarity index 100% rename from experiments/exp1-onechat2/model.py rename to experiments/exp01-onechat2/model.py diff --git a/experiments/exp1-onechat2/playbook.yaml b/experiments/exp01-onechat2/playbook.yaml similarity index 100% rename from experiments/exp1-onechat2/playbook.yaml rename to experiments/exp01-onechat2/playbook.yaml diff --git a/experiments/exp2-onechat10/README.md b/experiments/exp02-onechat10/README.md similarity index 100% rename from experiments/exp2-onechat10/README.md rename to experiments/exp02-onechat10/README.md diff --git a/experiments/exp2-onechat10/explanation.png b/experiments/exp02-onechat10/explanation.png similarity index 100% rename from experiments/exp2-onechat10/explanation.png rename to experiments/exp02-onechat10/explanation.png diff --git a/experiments/exp2-onechat10/explanation.puml b/experiments/exp02-onechat10/explanation.puml similarity index 100% rename from experiments/exp2-onechat10/explanation.puml rename to experiments/exp02-onechat10/explanation.puml diff --git a/experiments/exp2-onechat10/fledger.service b/experiments/exp02-onechat10/fledger.service similarity index 100% rename from experiments/exp2-onechat10/fledger.service rename to experiments/exp02-onechat10/fledger.service diff --git a/experiments/exp2-onechat10/flsignal.service b/experiments/exp02-onechat10/flsignal.service similarity index 100% rename from experiments/exp2-onechat10/flsignal.service rename to experiments/exp02-onechat10/flsignal.service diff --git a/experiments/exp2-onechat10/generate-env.sh b/experiments/exp02-onechat10/generate-env.sh similarity index 100% rename from experiments/exp2-onechat10/generate-env.sh rename to experiments/exp02-onechat10/generate-env.sh diff --git a/experiments/exp2-onechat10/hosts b/experiments/exp02-onechat10/hosts similarity index 100% rename from experiments/exp2-onechat10/hosts rename to experiments/exp02-onechat10/hosts diff --git a/experiments/exp2-onechat10/model.py b/experiments/exp02-onechat10/model.py similarity index 100% rename from experiments/exp2-onechat10/model.py rename to experiments/exp02-onechat10/model.py diff --git a/experiments/exp2-onechat10/playbook.yaml b/experiments/exp02-onechat10/playbook.yaml similarity index 100% rename from experiments/exp2-onechat10/playbook.yaml rename to experiments/exp02-onechat10/playbook.yaml diff --git a/experiments/exp3-onechat2x5/README.md b/experiments/exp03-onechat2x5/README.md similarity index 100% rename from experiments/exp3-onechat2x5/README.md rename to experiments/exp03-onechat2x5/README.md diff --git a/experiments/exp3-onechat2x5/fledger.service b/experiments/exp03-onechat2x5/fledger.service similarity index 100% rename from experiments/exp3-onechat2x5/fledger.service rename to experiments/exp03-onechat2x5/fledger.service diff --git a/experiments/exp3-onechat2x5/flsignal.service b/experiments/exp03-onechat2x5/flsignal.service similarity index 100% rename from experiments/exp3-onechat2x5/flsignal.service rename to experiments/exp03-onechat2x5/flsignal.service diff --git a/experiments/exp3-onechat2x5/generate-env.sh b/experiments/exp03-onechat2x5/generate-env.sh similarity index 100% rename from experiments/exp3-onechat2x5/generate-env.sh rename to experiments/exp03-onechat2x5/generate-env.sh diff --git a/experiments/exp3-onechat2x5/hosts b/experiments/exp03-onechat2x5/hosts similarity index 100% rename from experiments/exp3-onechat2x5/hosts rename to experiments/exp03-onechat2x5/hosts diff --git a/experiments/exp3-onechat2x5/model.py b/experiments/exp03-onechat2x5/model.py similarity index 100% rename from experiments/exp3-onechat2x5/model.py rename to experiments/exp03-onechat2x5/model.py diff --git a/experiments/exp3-onechat2x5/network.png b/experiments/exp03-onechat2x5/network.png similarity index 100% rename from experiments/exp3-onechat2x5/network.png rename to experiments/exp03-onechat2x5/network.png diff --git a/experiments/exp3-onechat2x5/network.puml b/experiments/exp03-onechat2x5/network.puml similarity index 100% rename from experiments/exp3-onechat2x5/network.puml rename to experiments/exp03-onechat2x5/network.puml diff --git a/experiments/exp3-onechat2x5/playbook.yaml b/experiments/exp03-onechat2x5/playbook.yaml similarity index 100% rename from experiments/exp3-onechat2x5/playbook.yaml rename to experiments/exp03-onechat2x5/playbook.yaml diff --git a/experiments/exp4-onechat2x25/README.md b/experiments/exp04-onechat2x25/README.md similarity index 100% rename from experiments/exp4-onechat2x25/README.md rename to experiments/exp04-onechat2x25/README.md diff --git a/experiments/exp4-onechat2x25/fledger.service b/experiments/exp04-onechat2x25/fledger.service similarity index 100% rename from experiments/exp4-onechat2x25/fledger.service rename to experiments/exp04-onechat2x25/fledger.service diff --git a/experiments/exp4-onechat2x25/flsignal.service b/experiments/exp04-onechat2x25/flsignal.service similarity index 100% rename from experiments/exp4-onechat2x25/flsignal.service rename to experiments/exp04-onechat2x25/flsignal.service diff --git a/experiments/exp4-onechat2x25/generate-env.sh b/experiments/exp04-onechat2x25/generate-env.sh similarity index 100% rename from experiments/exp4-onechat2x25/generate-env.sh rename to experiments/exp04-onechat2x25/generate-env.sh diff --git a/experiments/exp4-onechat2x25/hosts b/experiments/exp04-onechat2x25/hosts similarity index 100% rename from experiments/exp4-onechat2x25/hosts rename to experiments/exp04-onechat2x25/hosts diff --git a/experiments/exp4-onechat2x25/model.py b/experiments/exp04-onechat2x25/model.py similarity index 100% rename from experiments/exp4-onechat2x25/model.py rename to experiments/exp04-onechat2x25/model.py diff --git a/experiments/exp4-onechat2x25/network.png b/experiments/exp04-onechat2x25/network.png similarity index 100% rename from experiments/exp4-onechat2x25/network.png rename to experiments/exp04-onechat2x25/network.png diff --git a/experiments/exp4-onechat2x25/network.puml b/experiments/exp04-onechat2x25/network.puml similarity index 100% rename from experiments/exp4-onechat2x25/network.puml rename to experiments/exp04-onechat2x25/network.puml diff --git a/experiments/exp4-onechat2x25/playbook.yaml b/experiments/exp04-onechat2x25/playbook.yaml similarity index 100% rename from experiments/exp4-onechat2x25/playbook.yaml rename to experiments/exp04-onechat2x25/playbook.yaml diff --git a/experiments/exp5-onechat4x25/README.md b/experiments/exp05-onechat4x25/README.md similarity index 100% rename from experiments/exp5-onechat4x25/README.md rename to experiments/exp05-onechat4x25/README.md diff --git a/experiments/exp5-onechat4x25/fledger.service b/experiments/exp05-onechat4x25/fledger.service similarity index 100% rename from experiments/exp5-onechat4x25/fledger.service rename to experiments/exp05-onechat4x25/fledger.service diff --git a/experiments/exp5-onechat4x25/flsignal.service b/experiments/exp05-onechat4x25/flsignal.service similarity index 100% rename from experiments/exp5-onechat4x25/flsignal.service rename to experiments/exp05-onechat4x25/flsignal.service diff --git a/experiments/exp5-onechat4x25/generate-env.sh b/experiments/exp05-onechat4x25/generate-env.sh similarity index 100% rename from experiments/exp5-onechat4x25/generate-env.sh rename to experiments/exp05-onechat4x25/generate-env.sh diff --git a/experiments/exp5-onechat4x25/hosts b/experiments/exp05-onechat4x25/hosts similarity index 100% rename from experiments/exp5-onechat4x25/hosts rename to experiments/exp05-onechat4x25/hosts diff --git a/experiments/exp5-onechat4x25/model.py b/experiments/exp05-onechat4x25/model.py similarity index 100% rename from experiments/exp5-onechat4x25/model.py rename to experiments/exp05-onechat4x25/model.py diff --git a/experiments/exp5-onechat4x25/network.png b/experiments/exp05-onechat4x25/network.png similarity index 100% rename from experiments/exp5-onechat4x25/network.png rename to experiments/exp05-onechat4x25/network.png diff --git a/experiments/exp5-onechat4x25/network.puml b/experiments/exp05-onechat4x25/network.puml similarity index 100% rename from experiments/exp5-onechat4x25/network.puml rename to experiments/exp05-onechat4x25/network.puml diff --git a/experiments/exp5-onechat4x25/playbook.yaml b/experiments/exp05-onechat4x25/playbook.yaml similarity index 100% rename from experiments/exp5-onechat4x25/playbook.yaml rename to experiments/exp05-onechat4x25/playbook.yaml From 0a05ace3875301321e4a6b9abe981001f3ded752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 14 Apr 2025 18:54:19 +0200 Subject: [PATCH 21/43] feat: exp10 dht2x25 --- cli/fledger/src/simulation.rs | 11 ++ experiments/exp10-dht2x25/README.md | 10 ++ experiments/exp10-dht2x25/fledger.service | 23 ++++ experiments/exp10-dht2x25/flrealm.service | 23 ++++ experiments/exp10-dht2x25/flsignal.service | 13 ++ experiments/exp10-dht2x25/generate-env.sh | 21 +++ experiments/exp10-dht2x25/hosts | 54 ++++++++ experiments/exp10-dht2x25/model.py | 32 +++++ experiments/exp10-dht2x25/network.png | Bin 0 -> 11060 bytes experiments/exp10-dht2x25/network.puml | 28 ++++ experiments/exp10-dht2x25/playbook.yaml | 143 +++++++++++++++++++++ 11 files changed, 358 insertions(+) create mode 100644 experiments/exp10-dht2x25/README.md create mode 100644 experiments/exp10-dht2x25/fledger.service create mode 100644 experiments/exp10-dht2x25/flrealm.service create mode 100644 experiments/exp10-dht2x25/flsignal.service create mode 100755 experiments/exp10-dht2x25/generate-env.sh create mode 100644 experiments/exp10-dht2x25/hosts create mode 100644 experiments/exp10-dht2x25/model.py create mode 100644 experiments/exp10-dht2x25/network.png create mode 100644 experiments/exp10-dht2x25/network.puml create mode 100644 experiments/exp10-dht2x25/playbook.yaml diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index c6493f90..a9bcd836 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -26,6 +26,8 @@ pub enum SimulationSubcommand { #[arg(long)] recv_msg: Option, }, + + DhtJoinRealm {}, } pub struct SimulationHandler {} @@ -36,6 +38,7 @@ impl SimulationHandler { SimulationSubcommand::Chat { send_msg, recv_msg } => { Self::run_chat(f, command, send_msg, recv_msg).await } + SimulationSubcommand::DhtJoinRealm {} => Self::run_dht_join_realm(f).await, } } @@ -95,6 +98,14 @@ impl SimulationHandler { } } + async fn run_dht_join_realm(mut f: Fledger) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::DHTAvailable).await?; + log::info!("SIMULATION END"); + + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } + fn log_new_messages(f: &Fledger, acked_msg_ids: &mut Vec) { let chat_events = f.node.gossip.as_ref().unwrap().chat_events(); let chats: Vec<&Event> = chat_events diff --git a/experiments/exp10-dht2x25/README.md b/experiments/exp10-dht2x25/README.md new file mode 100644 index 00000000..cf231418 --- /dev/null +++ b/experiments/exp10-dht2x25/README.md @@ -0,0 +1,10 @@ +# Exp10 dht2x25 + +Two LANs of 25 nodes separated by a router. + +Single central node with: + +- The signaling server +- A node that creates a realm in that signaling server + +The 50 nodes then simply connect and join the realm. diff --git a/experiments/exp10-dht2x25/fledger.service b/experiments/exp10-dht2x25/fledger.service new file mode 100644 index 00000000..2eaf2dff --- /dev/null +++ b/experiments/exp10-dht2x25/fledger.service @@ -0,0 +1,23 @@ + +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger \ + --config /root/flnode \ + --name ${FLEDGER_NODE_NAME} \ + --disable-turn-stun \ + --bootwait-max 15000 \ + --signal-url ws://${FLEDGER_CENTRAL_HOST}:8765 \ + -v \ + simulation dht-join-realm +RemainAfterExit=yes +Restart=no +User=root +EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp10-dht2x25/flrealm.service b/experiments/exp10-dht2x25/flrealm.service new file mode 100644 index 00000000..6d978857 --- /dev/null +++ b/experiments/exp10-dht2x25/flrealm.service @@ -0,0 +1,23 @@ + +[Unit] +Description=Fledger node to create a realm +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger \ + --config /root/flrealm \ + --name flrealm \ + --disable-turn-stun \ + --signal-url ws://127.0.0.1:8765 \ + -v \ + realm create \ + 65535 \ + 65535 +RemainAfterExit=yes +Restart=no +User=root +StandardOutput=append:/var/log/flrealm + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp10-dht2x25/flsignal.service b/experiments/exp10-dht2x25/flsignal.service new file mode 100644 index 00000000..c1ea0035 --- /dev/null +++ b/experiments/exp10-dht2x25/flsignal.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger signaling server +After=network.target + +[Service] +Type=simple +ExecStart=/root/flsignal -v +User=root +Restart=no +StandardOutput=append:/var/log/flsignal + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp10-dht2x25/generate-env.sh b/experiments/exp10-dht2x25/generate-env.sh new file mode 100755 index 00000000..b78c57ec --- /dev/null +++ b/experiments/exp10-dht2x25/generate-env.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +mkdir -p env.systemd + +amount=50 + +for i in $(seq 0 $((amount - 1))); do + nodename="n${i}" + envfile="env.systemd/${nodename}.env" + + if test "$i" -lt 25; then + signalhost="10.0.0.128" + else + signalhost="10.0.1.128" + fi + + { + echo "FLEDGER_CENTRAL_HOST=${signalhost}" + echo "FLEDGER_NODE_NAME=${nodename}" + } >"$envfile" +done diff --git a/experiments/exp10-dht2x25/hosts b/experiments/exp10-dht2x25/hosts new file mode 100644 index 00000000..aaf34ab2 --- /dev/null +++ b/experiments/exp10-dht2x25/hosts @@ -0,0 +1,54 @@ +[nodes] +n0 +n1 +n2 +n3 +n4 +n5 +n6 +n7 +n8 +n9 +n10 +n11 +n12 +n13 +n14 +n15 +n16 +n17 +n18 +n19 +n20 +n21 +n22 +n23 +n24 +n25 +n26 +n27 +n28 +n29 +n30 +n31 +n32 +n33 +n34 +n35 +n36 +n37 +n38 +n39 +n40 +n41 +n42 +n43 +n44 +n45 +n46 +n47 +n48 +n49 + +[central] +central diff --git a/experiments/exp10-dht2x25/model.py b/experiments/exp10-dht2x25/model.py new file mode 100644 index 00000000..2947124f --- /dev/null +++ b/experiments/exp10-dht2x25/model.py @@ -0,0 +1,32 @@ +from mergexp import * + +net = Network('exp10', routing == static) + +def makeNode(i: int): + name = f"n{i}" + return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) + +sna = [makeNode(i) for i in range(25)] +snb = [makeNode(i) for i in range(25, 50)] + +router = net.node('router', proc.cores>=1, memory.capacity>=mb(512)) +central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) + +sna.extend([router, central]) +snb.extend([router, central]) + +linka = net.connect(sna, capacity==mbps(100), latency==ms(10)) +linkb = net.connect(snb, capacity==mbps(100), latency==ms(10)) + +linka[router].socket.addrs = ip4("10.0.0.1/24") +linkb[router].socket.addrs = ip4("10.0.1.1/24") + +linka[central].socket.addrs = ip4("10.0.0.128/24") +linkb[central].socket.addrs = ip4("10.0.1.128/24") + +for i in range(25): + suffix = str(i + 10) + linka[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") + linkb[snb[i]].socket.addrs = ip4(f"10.0.1.{suffix}/24") + +experiment(net) diff --git a/experiments/exp10-dht2x25/network.png b/experiments/exp10-dht2x25/network.png new file mode 100644 index 0000000000000000000000000000000000000000..08907d0f054e50195dc9639abc79b0be1f3d7d01 GIT binary patch literal 11060 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 ThM9pE8X+n-H5Ciw&7b@iJ4Z{B literal 0 HcmV?d00001 diff --git a/experiments/exp10-dht2x25/network.puml b/experiments/exp10-dht2x25/network.puml new file mode 100644 index 00000000..b171c2a5 --- /dev/null +++ b/experiments/exp10-dht2x25/network.puml @@ -0,0 +1,28 @@ +@startuml +nwdiag { + network sna { + width = full + address = 10.0.0.0/24 + + n0 [address = 10.0.0.2]; + nx_a [shape = collections, description = n1...n23]; + n24 [address = 10.0.0.26]; + + router [address = 10.0.0.1]; + central [address = 10.0.0.128]; + } + + network snb { + address = 10.0.1.0/24 + + n25 [address = 10.0.1.2]; + nx_b [shape = collections, description = n26...n48]; + n49 [address = 10.0.1.26]; + + router [address = 10.0.1.1]; + central [address = 10.0.1.128]; + } +} +@enduml + + diff --git a/experiments/exp10-dht2x25/playbook.yaml b/experiments/exp10-dht2x25/playbook.yaml new file mode 100644 index 00000000..9530d52a --- /dev/null +++ b/experiments/exp10-dht2x25/playbook.yaml @@ -0,0 +1,143 @@ +- name: Prepare ansible host + hosts: 127.0.0.1 + connection: local + tasks: + - name: Generate node environments + changed_when: true + ansible.builtin.command: + cmd: ./generate-env.sh + + - name: Create metrics directory + ansible.builtin.file: + path: metrics + state: directory + mode: "0755" + +- name: Prepare fledger nodes + hosts: nodes + become: true + tasks: + - name: Copy binary + ansible.builtin.copy: + src: ~/fledger + dest: ~/ + mode: preserve + + - name: Copy service + ansible.builtin.copy: + src: fledger.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Stop service + ansible.builtin.systemd_service: + name: fledger + state: stopped + daemon_reload: true + + - name: Copy environment + ansible.builtin.copy: + src: "env.systemd/{{ inventory_hostname }}.env" + dest: ~/env.systemd + mode: preserve + + - name: Remove old node and logs + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /root/flnode + - /var/log/fledger + + +- name: Prepare central node + hosts: central + become: true + tasks: + - name: Disable ip forwarding + ansible.posix.sysctl: + name: net.ipv4.ip_forward + value: 0 + sysctl_set: true + state: present + reload: true + + - name: Copy binaries + ansible.builtin.copy: + src: "{{ item }}" + dest: ~/ + mode: preserve + loop: + - ~/fledger + - ~/flsignal + + - name: Copy services + ansible.builtin.copy: + src: "{{ item }}" + dest: /etc/systemd/system/ + mode: preserve + loop: + - flsignal.service + - flrealm.service + + - name: Stop services + ansible.builtin.systemd_service: + name: "{{ item }}" + state: stopped + daemon_reload: true + loop: + - flsignal + - flrealm + + - name: Remove logs + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /var/log/flsignal + - /var/log/flrealm + + - name: Start services + ansible.builtin.systemd_service: + name: "{{ item }}" + state: restarted + loop: + - flsignal + - flrealm + +- name: Run the simulation + hosts: nodes + become: true + timeout: 300 + tasks: + - name: Start fledger + ansible.builtin.systemd_service: + name: fledger + state: restarted + daemon_reload: true + + - name: Wait for success trigger + ansible.builtin.wait_for: + path: /var/log/fledger + search_regex: "SIMULATION END" + +# Must remain separate so it runs even if the simulation fails +- name: Download metrics + hosts: nodes + become: true + tasks: + - name: Download metrics + ansible.builtin.fetch: + src: /tmp/out.metrics + flat: true + dest: "metrics/{{ inventory_hostname }}.metrics" + +- name: Assemble metrics into one file + hosts: 127.0.0.1 + connection: local + tasks: + - name: Assemble metrics + ansible.builtin.assemble: + src: metrics + dest: assembled.metrics + mode: "644" From 97d23185106a1e7eb72e480f885f5ec532e01705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 14 Apr 2025 19:01:47 +0200 Subject: [PATCH 22/43] feat: exp11 dht4x25 --- experiments/exp11-dht4x25/README.md | 10 ++ experiments/exp11-dht4x25/fledger.service | 23 ++++ experiments/exp11-dht4x25/flrealm.service | 23 ++++ experiments/exp11-dht4x25/flsignal.service | 13 ++ experiments/exp11-dht4x25/generate-env.sh | 17 +++ experiments/exp11-dht4x25/hosts | 104 +++++++++++++++ experiments/exp11-dht4x25/model.py | 58 +++++++++ experiments/exp11-dht4x25/network.png | Bin 0 -> 11060 bytes experiments/exp11-dht4x25/network.puml | 28 ++++ experiments/exp11-dht4x25/playbook.yaml | 143 +++++++++++++++++++++ 10 files changed, 419 insertions(+) create mode 100644 experiments/exp11-dht4x25/README.md create mode 100644 experiments/exp11-dht4x25/fledger.service create mode 100644 experiments/exp11-dht4x25/flrealm.service create mode 100644 experiments/exp11-dht4x25/flsignal.service create mode 100755 experiments/exp11-dht4x25/generate-env.sh create mode 100644 experiments/exp11-dht4x25/hosts create mode 100644 experiments/exp11-dht4x25/model.py create mode 100644 experiments/exp11-dht4x25/network.png create mode 100644 experiments/exp11-dht4x25/network.puml create mode 100644 experiments/exp11-dht4x25/playbook.yaml diff --git a/experiments/exp11-dht4x25/README.md b/experiments/exp11-dht4x25/README.md new file mode 100644 index 00000000..53470347 --- /dev/null +++ b/experiments/exp11-dht4x25/README.md @@ -0,0 +1,10 @@ +# Exp11 dht4x25 + +Two LANs of 25 nodes separated by a router. + +Single central node with: + +- The signaling server +- A node that creates a realm in that signaling server + +The 50 nodes then simply connect and join the realm. diff --git a/experiments/exp11-dht4x25/fledger.service b/experiments/exp11-dht4x25/fledger.service new file mode 100644 index 00000000..2eaf2dff --- /dev/null +++ b/experiments/exp11-dht4x25/fledger.service @@ -0,0 +1,23 @@ + +[Unit] +Description=Fledger node +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger \ + --config /root/flnode \ + --name ${FLEDGER_NODE_NAME} \ + --disable-turn-stun \ + --bootwait-max 15000 \ + --signal-url ws://${FLEDGER_CENTRAL_HOST}:8765 \ + -v \ + simulation dht-join-realm +RemainAfterExit=yes +Restart=no +User=root +EnvironmentFile=/root/env.systemd +StandardOutput=append:/var/log/fledger + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp11-dht4x25/flrealm.service b/experiments/exp11-dht4x25/flrealm.service new file mode 100644 index 00000000..6d978857 --- /dev/null +++ b/experiments/exp11-dht4x25/flrealm.service @@ -0,0 +1,23 @@ + +[Unit] +Description=Fledger node to create a realm +After=network.target + +[Service] +Type=simple +ExecStart=/root/fledger \ + --config /root/flrealm \ + --name flrealm \ + --disable-turn-stun \ + --signal-url ws://127.0.0.1:8765 \ + -v \ + realm create \ + 65535 \ + 65535 +RemainAfterExit=yes +Restart=no +User=root +StandardOutput=append:/var/log/flrealm + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp11-dht4x25/flsignal.service b/experiments/exp11-dht4x25/flsignal.service new file mode 100644 index 00000000..c1ea0035 --- /dev/null +++ b/experiments/exp11-dht4x25/flsignal.service @@ -0,0 +1,13 @@ +[Unit] +Description=Fledger signaling server +After=network.target + +[Service] +Type=simple +ExecStart=/root/flsignal -v +User=root +Restart=no +StandardOutput=append:/var/log/flsignal + +[Install] +WantedBy=multi-user.target diff --git a/experiments/exp11-dht4x25/generate-env.sh b/experiments/exp11-dht4x25/generate-env.sh new file mode 100755 index 00000000..5837fc87 --- /dev/null +++ b/experiments/exp11-dht4x25/generate-env.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +mkdir -p env.systemd + +amount=100 + +for i in $(seq 0 $((amount - 1))); do + nodename="n${i}" + envfile="env.systemd/${nodename}.env" + + signalhost="10.0.128.128" + + { + echo "FLEDGER_CENTRAL_HOST=${signalhost}" + echo "FLEDGER_NODE_NAME=${nodename}" + } >"$envfile" +done diff --git a/experiments/exp11-dht4x25/hosts b/experiments/exp11-dht4x25/hosts new file mode 100644 index 00000000..685648cd --- /dev/null +++ b/experiments/exp11-dht4x25/hosts @@ -0,0 +1,104 @@ +[nodes] +n0 +n1 +n2 +n3 +n4 +n5 +n6 +n7 +n8 +n9 +n10 +n11 +n12 +n13 +n14 +n15 +n16 +n17 +n18 +n19 +n20 +n21 +n22 +n23 +n24 +n25 +n26 +n27 +n28 +n29 +n30 +n31 +n32 +n33 +n34 +n35 +n36 +n37 +n38 +n39 +n40 +n41 +n42 +n43 +n44 +n45 +n46 +n47 +n48 +n49 +n50 +n51 +n52 +n53 +n54 +n55 +n56 +n57 +n58 +n59 +n60 +n61 +n62 +n63 +n64 +n65 +n66 +n67 +n68 +n69 +n70 +n71 +n72 +n73 +n74 +n75 +n76 +n77 +n78 +n79 +n80 +n81 +n82 +n83 +n84 +n85 +n86 +n87 +n88 +n89 +n90 +n91 +n92 +n93 +n94 +n95 +n96 +n97 +n98 +n99 + +[central] +central diff --git a/experiments/exp11-dht4x25/model.py b/experiments/exp11-dht4x25/model.py new file mode 100644 index 00000000..b5ab8e3a --- /dev/null +++ b/experiments/exp11-dht4x25/model.py @@ -0,0 +1,58 @@ +from mergexp import * + +net = Network('exp11', routing == static) + +def makeNode(i: int): + name = f"n{i}" + return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) + +lans = [ + [makeNode(i) for i in range(25)], + [makeNode(i) for i in range(25, 50)], + [makeNode(i) for i in range(50, 75)], + [makeNode(i) for i in range(75, 100)] +] + +router01 = net.node('router01', proc.cores>=1, memory.capacity>=mb(512)) +router12 = net.node('router12', proc.cores>=1, memory.capacity>=mb(512)) +router23 = net.node('router23', proc.cores>=1, memory.capacity>=mb(512)) + +central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) + +lans[0].extend([router01]) +lans[1].extend([router01, router12]) +lans[2].extend([router12, router23]) +lans[3].extend([router23]) + +links = [ + #net.connect(lans[i], capacity==mbps(100), latency==ms(10)) + net.connect(lans[i]) # atm capacity and latency break sphere + for i in range(len(lans)) +] + + +links[0][router01].socket.addrs = ip4(f"10.0.0.2/24") + +links[1][router01].socket.addrs = ip4(f"10.0.1.1/24") +links[1][router12].socket.addrs = ip4(f"10.0.1.2/24") + +links[2][router12].socket.addrs = ip4(f"10.0.2.1/24") +links[2][router23].socket.addrs = ip4(f"10.0.2.2/24") + +links[3][router23].socket.addrs = ip4(f"10.0.3.1/24") + +centralLan = [router12, central] +#centralLink = net.connect(centralLan, capacity==mbps(100), latency==ms(10)) +centralLink = net.connect(centralLan) + +centralLink[central].socket.addrs = ip4(f"10.0.128.128/24") +centralLink[router12].socket.addrs = ip4(f"10.0.128.1/24") + +for i in range(len(links)): + lan = lans[i] + link = links[i] + for j in range(25): + n = str(j + 10) + link[lan[j]].socket.addrs = ip4(f"10.0.{i}.{n}/24") + +experiment(net) diff --git a/experiments/exp11-dht4x25/network.png b/experiments/exp11-dht4x25/network.png new file mode 100644 index 0000000000000000000000000000000000000000..08907d0f054e50195dc9639abc79b0be1f3d7d01 GIT binary patch literal 11060 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 ThM9pE8X+n-H5Ciw&7b@iJ4Z{B literal 0 HcmV?d00001 diff --git a/experiments/exp11-dht4x25/network.puml b/experiments/exp11-dht4x25/network.puml new file mode 100644 index 00000000..b171c2a5 --- /dev/null +++ b/experiments/exp11-dht4x25/network.puml @@ -0,0 +1,28 @@ +@startuml +nwdiag { + network sna { + width = full + address = 10.0.0.0/24 + + n0 [address = 10.0.0.2]; + nx_a [shape = collections, description = n1...n23]; + n24 [address = 10.0.0.26]; + + router [address = 10.0.0.1]; + central [address = 10.0.0.128]; + } + + network snb { + address = 10.0.1.0/24 + + n25 [address = 10.0.1.2]; + nx_b [shape = collections, description = n26...n48]; + n49 [address = 10.0.1.26]; + + router [address = 10.0.1.1]; + central [address = 10.0.1.128]; + } +} +@enduml + + diff --git a/experiments/exp11-dht4x25/playbook.yaml b/experiments/exp11-dht4x25/playbook.yaml new file mode 100644 index 00000000..9530d52a --- /dev/null +++ b/experiments/exp11-dht4x25/playbook.yaml @@ -0,0 +1,143 @@ +- name: Prepare ansible host + hosts: 127.0.0.1 + connection: local + tasks: + - name: Generate node environments + changed_when: true + ansible.builtin.command: + cmd: ./generate-env.sh + + - name: Create metrics directory + ansible.builtin.file: + path: metrics + state: directory + mode: "0755" + +- name: Prepare fledger nodes + hosts: nodes + become: true + tasks: + - name: Copy binary + ansible.builtin.copy: + src: ~/fledger + dest: ~/ + mode: preserve + + - name: Copy service + ansible.builtin.copy: + src: fledger.service + dest: /etc/systemd/system/ + mode: preserve + + - name: Stop service + ansible.builtin.systemd_service: + name: fledger + state: stopped + daemon_reload: true + + - name: Copy environment + ansible.builtin.copy: + src: "env.systemd/{{ inventory_hostname }}.env" + dest: ~/env.systemd + mode: preserve + + - name: Remove old node and logs + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /root/flnode + - /var/log/fledger + + +- name: Prepare central node + hosts: central + become: true + tasks: + - name: Disable ip forwarding + ansible.posix.sysctl: + name: net.ipv4.ip_forward + value: 0 + sysctl_set: true + state: present + reload: true + + - name: Copy binaries + ansible.builtin.copy: + src: "{{ item }}" + dest: ~/ + mode: preserve + loop: + - ~/fledger + - ~/flsignal + + - name: Copy services + ansible.builtin.copy: + src: "{{ item }}" + dest: /etc/systemd/system/ + mode: preserve + loop: + - flsignal.service + - flrealm.service + + - name: Stop services + ansible.builtin.systemd_service: + name: "{{ item }}" + state: stopped + daemon_reload: true + loop: + - flsignal + - flrealm + + - name: Remove logs + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /var/log/flsignal + - /var/log/flrealm + + - name: Start services + ansible.builtin.systemd_service: + name: "{{ item }}" + state: restarted + loop: + - flsignal + - flrealm + +- name: Run the simulation + hosts: nodes + become: true + timeout: 300 + tasks: + - name: Start fledger + ansible.builtin.systemd_service: + name: fledger + state: restarted + daemon_reload: true + + - name: Wait for success trigger + ansible.builtin.wait_for: + path: /var/log/fledger + search_regex: "SIMULATION END" + +# Must remain separate so it runs even if the simulation fails +- name: Download metrics + hosts: nodes + become: true + tasks: + - name: Download metrics + ansible.builtin.fetch: + src: /tmp/out.metrics + flat: true + dest: "metrics/{{ inventory_hostname }}.metrics" + +- name: Assemble metrics into one file + hosts: 127.0.0.1 + connection: local + tasks: + - name: Assemble metrics + ansible.builtin.assemble: + src: metrics + dest: assembled.metrics + mode: "644" From 8d549e79bcc2d5dfa84945eb57c5990adaea681d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sat, 26 Apr 2025 18:29:47 +0200 Subject: [PATCH 23/43] feat: remove experiments (moved to dedicated repo) --- .gitignore | 1 - Makefile | 18 +-- .../deploy-binaries.sh => deploy-binaries.sh | 8 +- experiments/README.md | 111 -------------- experiments/deploy-experiments.sh | 9 -- experiments/exp01-onechat2/README.md | 9 -- experiments/exp01-onechat2/explanation.png | Bin 14017 -> 0 bytes experiments/exp01-onechat2/explanation.puml | 18 --- .../exp01-onechat2/fledger-recv.service | 13 -- .../exp01-onechat2/fledger-send.service | 13 -- experiments/exp01-onechat2/flsignal.service | 12 -- experiments/exp01-onechat2/hosts | 3 - experiments/exp01-onechat2/model.py | 14 -- experiments/exp01-onechat2/playbook.yaml | 68 --------- experiments/exp02-onechat10/README.md | 9 -- experiments/exp02-onechat10/explanation.png | Bin 49801 -> 0 bytes experiments/exp02-onechat10/explanation.puml | 39 ----- experiments/exp02-onechat10/fledger.service | 15 -- experiments/exp02-onechat10/flsignal.service | 13 -- experiments/exp02-onechat10/generate-env.sh | 29 ---- experiments/exp02-onechat10/hosts | 11 -- experiments/exp02-onechat10/model.py | 17 --- experiments/exp02-onechat10/playbook.yaml | 74 --------- experiments/exp03-onechat2x5/README.md | 9 -- experiments/exp03-onechat2x5/fledger.service | 16 -- experiments/exp03-onechat2x5/flsignal.service | 13 -- experiments/exp03-onechat2x5/generate-env.sh | 38 ----- experiments/exp03-onechat2x5/hosts | 14 -- experiments/exp03-onechat2x5/model.py | 34 ----- experiments/exp03-onechat2x5/network.png | Bin 12760 -> 0 bytes experiments/exp03-onechat2x5/network.puml | 31 ---- experiments/exp03-onechat2x5/playbook.yaml | 106 ------------- experiments/exp04-onechat2x25/README.md | 4 - experiments/exp04-onechat2x25/fledger.service | 27 ---- .../exp04-onechat2x25/flsignal.service | 13 -- experiments/exp04-onechat2x25/generate-env.sh | 38 ----- experiments/exp04-onechat2x25/hosts | 54 ------- experiments/exp04-onechat2x25/model.py | 32 ---- experiments/exp04-onechat2x25/network.png | Bin 11060 -> 0 bytes experiments/exp04-onechat2x25/network.puml | 28 ---- experiments/exp04-onechat2x25/playbook.yaml | 128 ---------------- experiments/exp05-onechat4x25/README.md | 9 -- experiments/exp05-onechat4x25/fledger.service | 26 ---- .../exp05-onechat4x25/flsignal.service | 13 -- experiments/exp05-onechat4x25/generate-env.sh | 43 ------ experiments/exp05-onechat4x25/hosts | 104 ------------- experiments/exp05-onechat4x25/model.py | 56 ------- experiments/exp05-onechat4x25/network.png | Bin 26144 -> 0 bytes experiments/exp05-onechat4x25/network.puml | 52 ------- experiments/exp05-onechat4x25/playbook.yaml | 128 ---------------- experiments/exp10-dht2x25/README.md | 10 -- experiments/exp10-dht2x25/fledger.service | 23 --- experiments/exp10-dht2x25/flrealm.service | 23 --- experiments/exp10-dht2x25/flsignal.service | 13 -- experiments/exp10-dht2x25/generate-env.sh | 21 --- experiments/exp10-dht2x25/hosts | 54 ------- experiments/exp10-dht2x25/model.py | 32 ---- experiments/exp10-dht2x25/network.png | Bin 11060 -> 0 bytes experiments/exp10-dht2x25/network.puml | 28 ---- experiments/exp10-dht2x25/playbook.yaml | 143 ------------------ experiments/exp11-dht4x25/README.md | 10 -- experiments/exp11-dht4x25/fledger.service | 23 --- experiments/exp11-dht4x25/flrealm.service | 23 --- experiments/exp11-dht4x25/flsignal.service | 13 -- experiments/exp11-dht4x25/generate-env.sh | 17 --- experiments/exp11-dht4x25/hosts | 104 ------------- experiments/exp11-dht4x25/model.py | 58 ------- experiments/exp11-dht4x25/network.png | Bin 11060 -> 0 bytes experiments/exp11-dht4x25/network.puml | 28 ---- experiments/exp11-dht4x25/playbook.yaml | 143 ------------------ 70 files changed, 9 insertions(+), 2277 deletions(-) rename experiments/deploy-binaries.sh => deploy-binaries.sh (56%) delete mode 100644 experiments/README.md delete mode 100755 experiments/deploy-experiments.sh delete mode 100644 experiments/exp01-onechat2/README.md delete mode 100644 experiments/exp01-onechat2/explanation.png delete mode 100644 experiments/exp01-onechat2/explanation.puml delete mode 100644 experiments/exp01-onechat2/fledger-recv.service delete mode 100644 experiments/exp01-onechat2/fledger-send.service delete mode 100644 experiments/exp01-onechat2/flsignal.service delete mode 100644 experiments/exp01-onechat2/hosts delete mode 100644 experiments/exp01-onechat2/model.py delete mode 100644 experiments/exp01-onechat2/playbook.yaml delete mode 100644 experiments/exp02-onechat10/README.md delete mode 100644 experiments/exp02-onechat10/explanation.png delete mode 100644 experiments/exp02-onechat10/explanation.puml delete mode 100644 experiments/exp02-onechat10/fledger.service delete mode 100644 experiments/exp02-onechat10/flsignal.service delete mode 100755 experiments/exp02-onechat10/generate-env.sh delete mode 100644 experiments/exp02-onechat10/hosts delete mode 100644 experiments/exp02-onechat10/model.py delete mode 100644 experiments/exp02-onechat10/playbook.yaml delete mode 100644 experiments/exp03-onechat2x5/README.md delete mode 100644 experiments/exp03-onechat2x5/fledger.service delete mode 100644 experiments/exp03-onechat2x5/flsignal.service delete mode 100755 experiments/exp03-onechat2x5/generate-env.sh delete mode 100644 experiments/exp03-onechat2x5/hosts delete mode 100644 experiments/exp03-onechat2x5/model.py delete mode 100644 experiments/exp03-onechat2x5/network.png delete mode 100644 experiments/exp03-onechat2x5/network.puml delete mode 100644 experiments/exp03-onechat2x5/playbook.yaml delete mode 100644 experiments/exp04-onechat2x25/README.md delete mode 100644 experiments/exp04-onechat2x25/fledger.service delete mode 100644 experiments/exp04-onechat2x25/flsignal.service delete mode 100755 experiments/exp04-onechat2x25/generate-env.sh delete mode 100644 experiments/exp04-onechat2x25/hosts delete mode 100644 experiments/exp04-onechat2x25/model.py delete mode 100644 experiments/exp04-onechat2x25/network.png delete mode 100644 experiments/exp04-onechat2x25/network.puml delete mode 100644 experiments/exp04-onechat2x25/playbook.yaml delete mode 100644 experiments/exp05-onechat4x25/README.md delete mode 100644 experiments/exp05-onechat4x25/fledger.service delete mode 100644 experiments/exp05-onechat4x25/flsignal.service delete mode 100755 experiments/exp05-onechat4x25/generate-env.sh delete mode 100644 experiments/exp05-onechat4x25/hosts delete mode 100644 experiments/exp05-onechat4x25/model.py delete mode 100644 experiments/exp05-onechat4x25/network.png delete mode 100644 experiments/exp05-onechat4x25/network.puml delete mode 100644 experiments/exp05-onechat4x25/playbook.yaml delete mode 100644 experiments/exp10-dht2x25/README.md delete mode 100644 experiments/exp10-dht2x25/fledger.service delete mode 100644 experiments/exp10-dht2x25/flrealm.service delete mode 100644 experiments/exp10-dht2x25/flsignal.service delete mode 100755 experiments/exp10-dht2x25/generate-env.sh delete mode 100644 experiments/exp10-dht2x25/hosts delete mode 100644 experiments/exp10-dht2x25/model.py delete mode 100644 experiments/exp10-dht2x25/network.png delete mode 100644 experiments/exp10-dht2x25/network.puml delete mode 100644 experiments/exp10-dht2x25/playbook.yaml delete mode 100644 experiments/exp11-dht4x25/README.md delete mode 100644 experiments/exp11-dht4x25/fledger.service delete mode 100644 experiments/exp11-dht4x25/flrealm.service delete mode 100644 experiments/exp11-dht4x25/flsignal.service delete mode 100755 experiments/exp11-dht4x25/generate-env.sh delete mode 100644 experiments/exp11-dht4x25/hosts delete mode 100644 experiments/exp11-dht4x25/model.py delete mode 100644 experiments/exp11-dht4x25/network.png delete mode 100644 experiments/exp11-dht4x25/network.puml delete mode 100644 experiments/exp11-dht4x25/playbook.yaml diff --git a/.gitignore b/.gitignore index a60361fd..e1c46044 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ target-common/ femme dist/ *.code-workspace -assembled.metrics diff --git a/Makefile b/Makefile index f533ad78..08217b6b 100644 --- a/Makefile +++ b/Makefile @@ -85,18 +85,12 @@ kill: $(call PKILL,fledger) $(call PKILL,trunk serve) -devbox_check_or_abort: - @test "${DEVBOX_SHELL_ENABLED}" == "1" || (echo "Devbox not enabled. Aborting..."; exit 1) - build_cli: cd cli && cargo build -p fledger && cargo build -p flsignal build_cli_release: cd cli && cargo build --release -p fledger && cargo build --release -p flsignal -build_cli_release_musl: devbox_check_or_abort - cd cli && cargo build --release --target=x86_64-unknown-linux-musl -p fledger && cargo build --release --target=x86_64-unknown-linux-musl -p flsignal - build_web_release: cd flbrowser && trunk build --release @@ -129,13 +123,11 @@ docker_dev: docker push fledgre/$$cli:dev; \ done -deploy_experiments_binaries: build_cli_release_musl - @echo "INFO: run make deploy_experiments to rsync the experiment files." - @cd experiments && ./deploy-binaries.sh - -deploy_experiments: - @echo "INFO: run make deploy_experiments_binaries to recompile and upload the binaries." - @cd experiments && ./deploy-experiments.sh +deploy: + @test "${DEVBOX_SHELL_ENABLED}" == "1" || (echo "Devbox not enabled. Aborting..."; exit 1) + @echo "Building fledger with musl, then deploying to XDC..." + cd cli && cargo build --release --target=x86_64-unknown-linux-musl -p fledger && cargo build --release --target=x86_64-unknown-linux-musl -p flsignal + @./deploy-binaries.sh clean: for c in ${CARGOS}; do \ diff --git a/experiments/deploy-binaries.sh b/deploy-binaries.sh similarity index 56% rename from experiments/deploy-binaries.sh rename to deploy-binaries.sh index c33beef6..0b70e2cf 100755 --- a/experiments/deploy-binaries.sh +++ b/deploy-binaries.sh @@ -5,13 +5,13 @@ if test "$#" -ne 0; then exit 1 fi -if ! test -f "../target-common/x86_64-unknown-linux-musl/release/flsignal"; then +if ! test -f "target-common/x86_64-unknown-linux-musl/release/flsignal"; then echo "ERROR: you did not compile flsignal with musl. The experiment will not work." echo "Aborting..." exit 1 fi -if ! test -f "../target-common/x86_64-unknown-linux-musl/release/fledger"; then +if ! test -f "target-common/x86_64-unknown-linux-musl/release/fledger"; then echo "ERROR: you did not compile fledger with musl. The experiment will not work." echo "Aborting..." exit 1 @@ -19,6 +19,6 @@ fi echo "Copying fledger and flsignal binaries..." scp \ - ../target-common/x86_64-unknown-linux-musl/release/fledger \ - ../target-common/x86_64-unknown-linux-musl/release/flsignal \ + target-common/x86_64-unknown-linux-musl/release/fledger \ + target-common/x86_64-unknown-linux-musl/release/flsignal \ sphere-fledger: diff --git a/experiments/README.md b/experiments/README.md deleted file mode 100644 index 2791b610..00000000 --- a/experiments/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# MergeTB experiments - -## Generalities - -Those experiments are meant to be run on sphere. - -Each is composed of: - -- A README (`README.md`) -- A mergeTB model (`model.py`) -- An ansible playbook (`playbook.yaml`) -- The ansible inventory (`hosts`) -- Oneshot systemd services to run the actual nodes - -And sometimes: - -- A plantUML sequence diagram of the experiment (`explanation.puml`, `explanation.png`) -- A plantUML network diagram (`network.puml`, `network.png`) - -The services can either be ran synchronously (ansible blocks) or asynchronously -(ansible starts the node and goes to the next task). -Check `exp1-dummy` for an example of both -(fledger-send is blocking and fledger-recv is non-blocking). - -## Create hosts file for a new experiment - -When creating a new experiment, there is a simple (but imperfect) command to generate the hosts file. -It's `mrg nodes generate inventory expX.fledger.abehsser > hosts` - -- abehsser is the project name -- fledger the experiment name -- expX the reservation name). - -You'll need to modify it to: - -1. Group the nodes together in a \[nodes\] group. -2. Put the central server in a \[central\] group. -3. Remove the router. - -## Running experiments - -### Setup mergeTB - -Read the documentation and do the hello world. - -Then, create one XDC to be used for all experiments. -Use this SSH configuration (read the documentation before using it): - -```bash -Host sphere - Hostname jump.sphere-testbed.net - Port 2022 - User YOUR_USER - IdentityFile ~/.ssh/merge_key - ServerAliveInterval 30 - -Host sphere-fledger - ProxyJump sphere - IdentityFile ~/.ssh/merge_key - User YOUR_USER - Hostname SSH_NAME_FROM_SPHERE -``` - -Don't forget to replace the values for `YOUR_USER` and `SSH_NAME_FROM_SPHERE`. - -On the XDC, install ansible (use the ansible/ansible apt ppa!). - -Then install ansible's prometheus collection with -`ansible-galaxy collection install prometheus.prometheus` - -Configure ansible with this `~/.ansible.cfg` from the documentation : - -```bash -[defaults] -# don't check experiment node keys, if this is not set, you will have to -# explicitly accept the SSH key for each experiment node you run Ansible -# against -host_key_checking = False - -# configure up to 5 hosts in parallel -forks = 5 - -# tmp directory on the local non-shared filesystem. Useful when running ansible -# on multiple separate XDCs -local_tmp = /tmp/ansible/tmp - -[ssh_connection] - -# connection optimization that increases speed significantly -pipelining = True - -# control socket directory on the local non-shared filesystem. Useful when -# running ansible on multiple separate XDCs -control_path_dir = /tmp/ansible/cp -``` - -### Activate the nodes and attach the XDC - -Use the model.py to create the reservation and the activation (materialization). -Attach your XDC to the materialization. - -### Deploy and run - -- Go to the root of the project on your local machine. -- Activate devbox with `devbox shell`. -- Run `make deploy_experiments_binaries` to -compile and upload the binaries to the XDC. -- Run `make deploy_experiments` to upload the experiment files as well. -- SSH into your XDC. -- Go to the experiment's folder. -- Run `ansible-playbook -i hosts playbook.yaml` diff --git a/experiments/deploy-experiments.sh b/experiments/deploy-experiments.sh deleted file mode 100755 index 8dbc6f60..00000000 --- a/experiments/deploy-experiments.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -if test "$#" -ne 0; then - echo "usage: $0" - exit 1 -fi - -echo "Rsyncing experiment files..." -rsync -ah --info=progress2 ./ sphere-fledger:~/experiments/ diff --git a/experiments/exp01-onechat2/README.md b/experiments/exp01-onechat2/README.md deleted file mode 100644 index e9469a2b..00000000 --- a/experiments/exp01-onechat2/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Exp1 dummy - -Testing the waters. - -Run two nodes. On node A, send a message. On node B, wait for that message. - -If nodeB gets the message, the playbook succeeds. - -If the timeout of 10s is reached, the playbook fails. diff --git a/experiments/exp01-onechat2/explanation.png b/experiments/exp01-onechat2/explanation.png deleted file mode 100644 index a8aa1f86706ed1bf9fc995e3d49ecf2d48d2bcb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14017 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 diff --git a/experiments/exp01-onechat2/explanation.puml b/experiments/exp01-onechat2/explanation.puml deleted file mode 100644 index 5867247a..00000000 --- a/experiments/exp01-onechat2/explanation.puml +++ /dev/null @@ -1,18 +0,0 @@ -@startuml -participant "Node A" as A -participant "Node B" as B -hnote over A: signaling server -hnote over A: fledger (send) -hnote over B: fledger (recv) -rnote over A, B - Setup with signaling server -endrnote -A -> B: chat message -rnote over A, B - Timeout 10s -endrnote -rnote over B - //exit code 0// - //iff message received// -endrnote -@enduml diff --git a/experiments/exp01-onechat2/fledger-recv.service b/experiments/exp01-onechat2/fledger-recv.service deleted file mode 100644 index 370b78ca..00000000 --- a/experiments/exp01-onechat2/fledger-recv.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun simulation recv-chat "message" --print-new-messages -RemainAfterExit=yes -User=root -StandardOutput=append:/var/log/fledger-recv - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp01-onechat2/fledger-send.service b/experiments/exp01-onechat2/fledger-send.service deleted file mode 100644 index 20d95893..00000000 --- a/experiments/exp01-onechat2/fledger-send.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=oneshot -ExecStart=/root/fledger --config /root/flnode -vv -s ws://10.0.0.1:8765 --disable-turn-stun simulation send-chat "message" -RemainAfterExit=yes -User=root -StandardOutput=append:/var/log/fledger-send - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp01-onechat2/flsignal.service b/experiments/exp01-onechat2/flsignal.service deleted file mode 100644 index 787b6bad..00000000 --- a/experiments/exp01-onechat2/flsignal.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=Fledger signaling server -After=network.target - -[Service] -Type=oneshot -ExecStart=/root/flsignal -vvv -RemainAfterExit=yes -User=root - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp01-onechat2/hosts b/experiments/exp01-onechat2/hosts deleted file mode 100644 index 706e0289..00000000 --- a/experiments/exp01-onechat2/hosts +++ /dev/null @@ -1,3 +0,0 @@ -[all] -a -b diff --git a/experiments/exp01-onechat2/model.py b/experiments/exp01-onechat2/model.py deleted file mode 100644 index 47d5cc10..00000000 --- a/experiments/exp01-onechat2/model.py +++ /dev/null @@ -1,14 +0,0 @@ -from mergexp import * - -net = Network('two') - -def makeNode(name: str): - return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) - -sna = [makeNode(name) for name in ['a', 'b']] - -link = net.connect(sna) -link[sna[0]].socket.addrs = ip4('10.0.0.1/24') -link[sna[1]].socket.addrs = ip4('10.0.0.2/24') - -experiment(net) diff --git a/experiments/exp01-onechat2/playbook.yaml b/experiments/exp01-onechat2/playbook.yaml deleted file mode 100644 index d1c230dc..00000000 --- a/experiments/exp01-onechat2/playbook.yaml +++ /dev/null @@ -1,68 +0,0 @@ -- name: Setup for all nodes - hosts: all - become: true - tasks: - - name: Copy executable to nodes - ansible.builtin.copy: - src: "~/{{ item }}" - dest: ~/ - mode: preserve - loop: - - fledger - - flsignal - - - name: Remove config folders and logs - ansible.builtin.file: - path: '{{ item }}' - state: absent - loop: - - /root/flnode - - /var/log/fledger-recv - - /var/log/fledger-send - -- name: Setup node a - hosts: a - become: true - tasks: - - name: Copy signaling service - ansible.builtin.copy: - src: flsignal.service - dest: /etc/systemd/system/ - mode: preserve - - name: Copy fledger service (send) - ansible.builtin.copy: - src: fledger-send.service - dest: /etc/systemd/system/ - mode: preserve - - name: Start signaling server - ansible.builtin.systemd_service: - name: flsignal - state: restarted - daemon_reload: true - no_block: true - - name: Start fledger node (send) - ansible.builtin.systemd_service: - name: fledger-send - state: restarted - no_block: true - -- name: Setup node b - hosts: b - become: true - tasks: - - name: Copy fledger service (recv) - ansible.builtin.copy: - src: fledger-recv.service - dest: /etc/systemd/system/ - mode: preserve - - name: Start fledger node (recv) - ansible.builtin.systemd_service: - name: fledger-recv - state: restarted - daemon_reload: true - no_block: true - - name: Wait for message received on fledger node (recv) - ansible.builtin.wait_for: - path: /var/log/fledger-recv - search_regex: "RECV_CHAT_MSG TRIGGERED" - timeout: 10 diff --git a/experiments/exp02-onechat10/README.md b/experiments/exp02-onechat10/README.md deleted file mode 100644 index d55118a0..00000000 --- a/experiments/exp02-onechat10/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Exp2 dummy10 - -Run 10 nodes, fully interconnected. - -Each node broadcasts a message (destined to a single node), -and waits for a message from one specific other node. - -The playbook succeeds if all nodes receive their message, -and fails if the timeout of 120s is reached. diff --git a/experiments/exp02-onechat10/explanation.png b/experiments/exp02-onechat10/explanation.png deleted file mode 100644 index 1f2457c763ae14230f4680f01d0c6dab6ea3be25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49801 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 t` C: **broadcast** //"AtoC"// -B -> D: **broadcast** //"BtoD"// -C -> E: **broadcast** //"CtoE"// -D -> F: **broadcast** //"DtoF"// -E -> G: **broadcast** //"EtoG"// -F -> H: **broadcast** //"FtoH"// -G -> I: **broadcast** //"GtoI"// -H -> J: **broadcast** //"HtoJ"// -I -> A: **broadcast** //"ItoA"// -J -> B: **broadcast** //"JtoB"// - -rnote across - Timeout 120s -endrnote -rnote across - //exit code 0// - //iff corresponding message received// -endrnote -@enduml diff --git a/experiments/exp02-onechat10/fledger.service b/experiments/exp02-onechat10/fledger.service deleted file mode 100644 index bdaaf60a..00000000 --- a/experiments/exp02-onechat10/fledger.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=simple -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_FLSIGNAL_HOST}:8765 --name ${FLEDGER_NODE_NAME} --disable-turn-stun simulation --print-new-messages chat --send-msg ${FLEDGER_SEND_MSG} --recv-msg ${FLEDGER_RECV_MSG} -RemainAfterExit=yes -Restart=no -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp02-onechat10/flsignal.service b/experiments/exp02-onechat10/flsignal.service deleted file mode 100644 index c1ea0035..00000000 --- a/experiments/exp02-onechat10/flsignal.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Fledger signaling server -After=network.target - -[Service] -Type=simple -ExecStart=/root/flsignal -v -User=root -Restart=no -StandardOutput=append:/var/log/flsignal - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp02-onechat10/generate-env.sh b/experiments/exp02-onechat10/generate-env.sh deleted file mode 100755 index 881fdf2e..00000000 --- a/experiments/exp02-onechat10/generate-env.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -signalhost="10.0.0.1" - -mkdir -p env.systemd - -messages=() - -for i in $(seq 10); do - message="$(openssl rand -hex 16)" - messages+=("$message") -done - -for i in $(seq 0 9); do - j=$((("$i" + 2) % 10)) - n=$(("$i" + 97)) - nodename=$(echo $n | awk '{printf("%c",$1)}') - envfile="env.systemd/${nodename}.env" - send_msg="${messages[$j]}" - recv_msg="${messages[$i]}" - echo "FLEDGER_FLSIGNAL_HOST=${signalhost}" >"$envfile" - echo "FLEDGER_SEND_MSG=${send_msg}" >>"$envfile" - echo "FLEDGER_RECV_MSG=${recv_msg}" >>"$envfile" - echo "FLEDGER_NODE_NAME=${nodename}" >>"$envfile" - - echo "[node $nodename]" - echo " <- ${recv_msg} [$i]" - echo " -> ${send_msg} [$j]" -done diff --git a/experiments/exp02-onechat10/hosts b/experiments/exp02-onechat10/hosts deleted file mode 100644 index 6f026d0f..00000000 --- a/experiments/exp02-onechat10/hosts +++ /dev/null @@ -1,11 +0,0 @@ -[all] -a -b -c -d -e -f -g -h -i -j diff --git a/experiments/exp02-onechat10/model.py b/experiments/exp02-onechat10/model.py deleted file mode 100644 index fd04a997..00000000 --- a/experiments/exp02-onechat10/model.py +++ /dev/null @@ -1,17 +0,0 @@ -from mergexp import * - -net = Network('exp2') - -def makeNode(i: int): - name = chr(ord('a') + i) - return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) - -sna = [makeNode(i) for i in range(10)] - -link = net.connect(sna, capacity==mbps(1), latency==ms(10)) - -for i in range(10): - suffix = str(i + 1) - link[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") - -experiment(net) diff --git a/experiments/exp02-onechat10/playbook.yaml b/experiments/exp02-onechat10/playbook.yaml deleted file mode 100644 index 810b5056..00000000 --- a/experiments/exp02-onechat10/playbook.yaml +++ /dev/null @@ -1,74 +0,0 @@ -- name: Generate environments on ansible host - hosts: 127.0.0.1 - connection: local - tasks: - - name: Generate environemnts - changed_when: true - ansible.builtin.command: - cmd: ./generate-env.sh - -- name: Clean nodes and upload files (binaries and services) - hosts: all - become: true - tasks: - - name: Copy executable to nodes - ansible.builtin.copy: - src: "~/{{ item }}" - dest: ~/ - mode: preserve - loop: - - fledger - - flsignal - - - name: Copy fledger services - ansible.builtin.copy: - src: "{{ item }}" - dest: /etc/systemd/system/ - mode: preserve - loop: - - fledger.service - - - name: Remove config folders and logs - ansible.builtin.file: - path: '{{ item }}' - state: absent - loop: - - /root/flnode - - /var/log/fledger - - /var/log/flsignal - -- name: Run signaling server on node A - hosts: a - become: true - tasks: - - name: Copy signaling service - ansible.builtin.copy: - src: flsignal.service - dest: /etc/systemd/system/ - mode: preserve - - name: Start signaling server - ansible.builtin.systemd_service: - name: flsignal - state: restarted - daemon_reload: true - -- name: Upload env and run fleger on each node - hosts: all - become: true - tasks: - - name: Copy env file to nodes - ansible.builtin.copy: - src: "env.systemd/{{ inventory_hostname }}.env" - dest: ~/env.systemd - mode: preserve - - name: Start fledger node - ansible.builtin.systemd_service: - name: fledger - state: restarted - no_block: true - daemon_reload: true - - name: Wait for message received - ansible.builtin.wait_for: - path: /var/log/fledger - search_regex: "RECV_CHAT_MSG TRIGGERED" - timeout: 120 diff --git a/experiments/exp03-onechat2x5/README.md b/experiments/exp03-onechat2x5/README.md deleted file mode 100644 index 4be4a08d..00000000 --- a/experiments/exp03-onechat2x5/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Exp3 dummy10-router - -Same as exp2 but with two lans of 5 nodes separated by a router - -- Timeout for send is 10min, timeout for recv is 60sec. -- There is a central server hosting the signaling server and prometheus - -No nice plantuml diagram for the experiment here, -it's the same as exp2 but the topology changes. diff --git a/experiments/exp03-onechat2x5/fledger.service b/experiments/exp03-onechat2x5/fledger.service deleted file mode 100644 index 96f6f555..00000000 --- a/experiments/exp03-onechat2x5/fledger.service +++ /dev/null @@ -1,16 +0,0 @@ - -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=simple -ExecStart=/root/fledger --config /root/flnode -v -s ws://${FLEDGER_CENTRAL_HOST}:8765 --name ${FLEDGER_NODE_NAME} --disable-turn-stun simulation --print-new-messages chat --send-msg ${FLEDGER_SEND_MSG} --recv-msg ${FLEDGER_RECV_MSG} -RemainAfterExit=yes -Restart=no -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp03-onechat2x5/flsignal.service b/experiments/exp03-onechat2x5/flsignal.service deleted file mode 100644 index c1ea0035..00000000 --- a/experiments/exp03-onechat2x5/flsignal.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Fledger signaling server -After=network.target - -[Service] -Type=simple -ExecStart=/root/flsignal -v -User=root -Restart=no -StandardOutput=append:/var/log/flsignal - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp03-onechat2x5/generate-env.sh b/experiments/exp03-onechat2x5/generate-env.sh deleted file mode 100755 index 54cd6680..00000000 --- a/experiments/exp03-onechat2x5/generate-env.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -mkdir -p env.systemd - -#amount=100 -amount=10 - -messages=() - -for i in $(seq $amount); do - message="$(openssl rand -hex 16)" - messages+=("$message") -done - -for i in $(seq 0 $((amount - 1))); do - j=$((("$i" + 2) % amount)) - nodename="n${i}" - envfile="env.systemd/${nodename}.env" - send_msg="${messages[$j]}" - recv_msg="${messages[$i]}" - - if test "$i" -lt 5; then - signalhost="10.0.0.128" - else - signalhost="10.0.1.128" - fi - - { - echo "FLEDGER_CENTRAL_HOST=${signalhost}" - echo "FLEDGER_SEND_MSG=${send_msg}" - echo "FLEDGER_RECV_MSG=${recv_msg}" - echo "FLEDGER_NODE_NAME=${nodename}" - } >"$envfile" - - echo "[node $nodename]" - echo " <- ${recv_msg} [$i]" - echo " -> ${send_msg} [$j]" -done diff --git a/experiments/exp03-onechat2x5/hosts b/experiments/exp03-onechat2x5/hosts deleted file mode 100644 index 9bc112bc..00000000 --- a/experiments/exp03-onechat2x5/hosts +++ /dev/null @@ -1,14 +0,0 @@ -[nodes] -n0 -n1 -n2 -n3 -n4 -n5 -n6 -n7 -n8 -n9 - -[central] -central diff --git a/experiments/exp03-onechat2x5/model.py b/experiments/exp03-onechat2x5/model.py deleted file mode 100644 index 4dbe1b85..00000000 --- a/experiments/exp03-onechat2x5/model.py +++ /dev/null @@ -1,34 +0,0 @@ -from mergexp import * - -net = Network('exp3', routing == static) - -def makeNode(i: int): - name = f"n{i}" - return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) - -sna = [makeNode(i) for i in range(5)] -snb = [makeNode(i) for i in range(5, 10)] - -router = net.node('router', proc.cores>=1, memory.capacity>=mb(512)) -central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) - -sna.extend([router, central]) -snb.extend([router, central]) - -linka = net.connect(sna, capacity==mbps(1), latency==ms(10)) -linkb = net.connect(snb, capacity==mbps(1), latency==ms(10)) - -linka[router].socket.addrs = ip4("10.0.0.1/24") -linkb[router].socket.addrs = ip4("10.0.1.1/24") - -linka[central].socket.addrs = ip4("10.0.0.128/24") -linkb[central].socket.addrs = ip4("10.0.1.128/24") - - -for i in range(5): - suffix = str(i + 2) - linka[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") - linkb[snb[i]].socket.addrs = ip4(f"10.0.1.{suffix}/24") - - -experiment(net) diff --git a/experiments/exp03-onechat2x5/network.png b/experiments/exp03-onechat2x5/network.png deleted file mode 100644 index 155f137fd3d6534875465f4398f90b4932540e25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12760 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 V!Pn^|Otljz+)$OxmAUiyUjXi{1KR)q diff --git a/experiments/exp03-onechat2x5/network.puml b/experiments/exp03-onechat2x5/network.puml deleted file mode 100644 index a688354d..00000000 --- a/experiments/exp03-onechat2x5/network.puml +++ /dev/null @@ -1,31 +0,0 @@ -@startuml -nwdiag { - network sna { - width = full - address = 10.0.0.0/24 - - n0 [address = 10.0.0.2]; - n1 [address = 10.0.0.3]; - n2 [address = 10.0.0.4]; - n3 [address = 10.0.0.5]; - n4 [address = 10.0.0.6]; - - router [address = 10.0.0.1]; - central [address = 10.0.0.128]; - } - - network snb { - address = 10.0.1.0/24 - - n5 [address = 10.0.1.2]; - n6 [address = 10.0.1.3]; - n7 [address = 10.0.1.4]; - n8 [address = 10.0.1.5]; - n9 [address = 10.0.1.6]; - - router [address = 10.0.1.1]; - central [address = 10.0.1.128]; - } -} -@enduml - diff --git a/experiments/exp03-onechat2x5/playbook.yaml b/experiments/exp03-onechat2x5/playbook.yaml deleted file mode 100644 index 2b050be3..00000000 --- a/experiments/exp03-onechat2x5/playbook.yaml +++ /dev/null @@ -1,106 +0,0 @@ -- name: Prepare ansible host - hosts: 127.0.0.1 - connection: local - tasks: - - name: Generate node environments - changed_when: true - ansible.builtin.command: - cmd: ./generate-env.sh - - - name: Create metrics directory - ansible.builtin.file: - path: metrics - state: directory - -- name: Clean nodes and upload files (binaries and services) - hosts: nodes - become: true - tasks: - - name: Copy fledger binary to nodes - ansible.builtin.copy: - src: ~/fledger - dest: ~/ - mode: preserve - - - name: Copy fledger services - ansible.builtin.copy: - src: fledger.service - dest: /etc/systemd/system/ - mode: preserve - - - name: Remove config folders and log files - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /root/flnode - - /var/log/fledger - - /var/log/flsignal - -- name: Run signaling server on central node - hosts: central - become: true - tasks: - - name: Copy flsignal - ansible.builtin.copy: - src: ~/flsignal - dest: ~/ - mode: preserve - - name: Copy signaling service - ansible.builtin.copy: - src: flsignal.service - dest: /etc/systemd/system/ - mode: preserve - - name: Start signaling server - ansible.builtin.systemd_service: - name: flsignal - state: restarted - daemon_reload: true - - name: Disable ip forwarding - ansible.posix.sysctl: - name: net.ipv4.ip_forward - value: 0 - sysctl_set: true - state: present - reload: true - -- name: Upload env and run fleger on each node - hosts: nodes - become: true - tasks: - - name: Copy env file to nodes - ansible.builtin.copy: - src: "env.systemd/{{ inventory_hostname }}.env" - dest: ~/env.systemd - mode: preserve - - name: (Re)start fledger node - ansible.builtin.systemd_service: - name: fledger - state: restarted - no_block: true - daemon_reload: true - - name: Wait for message received on fledger node - ansible.builtin.wait_for: - path: /var/log/fledger - search_regex: "RECV_CHAT_MSG TRIGGERED" - timeout: 120 - -- name: Download metrics - hosts: nodes - become: true - tasks: - - name: Download metrics - ansible.builtin.fetch: - src: /tmp/out.metrics - flat: true - dest: "metrics/{{ inventory_hostname }}.metrics" - -- name: Assemble metrics into one file - hosts: 127.0.0.1 - connection: local - tasks: - - name: Assemble metrics - ansible.builtin.assemble: - src: metrics - dest: assembled.metrics - mode: "644" diff --git a/experiments/exp04-onechat2x25/README.md b/experiments/exp04-onechat2x25/README.md deleted file mode 100644 index 99343a9e..00000000 --- a/experiments/exp04-onechat2x25/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Exp3 dummy2x25 - -Two LANs of 25 nodes separated by a router. -Single central server for signaling diff --git a/experiments/exp04-onechat2x25/fledger.service b/experiments/exp04-onechat2x25/fledger.service deleted file mode 100644 index a160dcac..00000000 --- a/experiments/exp04-onechat2x25/fledger.service +++ /dev/null @@ -1,27 +0,0 @@ - -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=simple -ExecStart=/root/fledger \ - --config /root/flnode \ - --name ${FLEDGER_NODE_NAME} \ - --disable-turn-stun \ - --bootwait-max 15000 \ - --signal-url ws://${FLEDGER_CENTRAL_HOST}:8765 \ - -v \ - simulation \ - --print-new-messages \ - chat \ - --send-msg ${FLEDGER_SEND_MSG} \ - --recv-msg ${FLEDGER_RECV_MSG} -RemainAfterExit=yes -Restart=no -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp04-onechat2x25/flsignal.service b/experiments/exp04-onechat2x25/flsignal.service deleted file mode 100644 index c1ea0035..00000000 --- a/experiments/exp04-onechat2x25/flsignal.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Fledger signaling server -After=network.target - -[Service] -Type=simple -ExecStart=/root/flsignal -v -User=root -Restart=no -StandardOutput=append:/var/log/flsignal - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp04-onechat2x25/generate-env.sh b/experiments/exp04-onechat2x25/generate-env.sh deleted file mode 100755 index 3cb3c8a9..00000000 --- a/experiments/exp04-onechat2x25/generate-env.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -mkdir -p env.systemd - -#amount=100 -amount=50 - -messages=() - -for i in $(seq $amount); do - message="$(openssl rand -hex 16)" - messages+=("$message") -done - -for i in $(seq 0 $((amount - 1))); do - j=$((("$i" + 2) % amount)) - nodename="n${i}" - envfile="env.systemd/${nodename}.env" - send_msg="${messages[$j]}" - recv_msg="${messages[$i]}" - - if test "$i" -lt 25; then - signalhost="10.0.0.128" - else - signalhost="10.0.1.128" - fi - - { - echo "FLEDGER_CENTRAL_HOST=${signalhost}" - echo "FLEDGER_SEND_MSG=${send_msg}" - echo "FLEDGER_RECV_MSG=${recv_msg}" - echo "FLEDGER_NODE_NAME=${nodename}" - } >"$envfile" - - echo "[node $nodename]" - echo " <- ${recv_msg} [$i]" - echo " -> ${send_msg} [$j]" -done diff --git a/experiments/exp04-onechat2x25/hosts b/experiments/exp04-onechat2x25/hosts deleted file mode 100644 index aaf34ab2..00000000 --- a/experiments/exp04-onechat2x25/hosts +++ /dev/null @@ -1,54 +0,0 @@ -[nodes] -n0 -n1 -n2 -n3 -n4 -n5 -n6 -n7 -n8 -n9 -n10 -n11 -n12 -n13 -n14 -n15 -n16 -n17 -n18 -n19 -n20 -n21 -n22 -n23 -n24 -n25 -n26 -n27 -n28 -n29 -n30 -n31 -n32 -n33 -n34 -n35 -n36 -n37 -n38 -n39 -n40 -n41 -n42 -n43 -n44 -n45 -n46 -n47 -n48 -n49 - -[central] -central diff --git a/experiments/exp04-onechat2x25/model.py b/experiments/exp04-onechat2x25/model.py deleted file mode 100644 index c8d708f9..00000000 --- a/experiments/exp04-onechat2x25/model.py +++ /dev/null @@ -1,32 +0,0 @@ -from mergexp import * - -net = Network('exp4', routing == static) - -def makeNode(i: int): - name = f"n{i}" - return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) - -sna = [makeNode(i) for i in range(25)] -snb = [makeNode(i) for i in range(25, 50)] - -router = net.node('router', proc.cores>=1, memory.capacity>=mb(512)) -central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) - -sna.extend([router, central]) -snb.extend([router, central]) - -linka = net.connect(sna, capacity==mbps(100), latency==ms(10)) -linkb = net.connect(snb, capacity==mbps(100), latency==ms(10)) - -linka[router].socket.addrs = ip4("10.0.0.1/24") -linkb[router].socket.addrs = ip4("10.0.1.1/24") - -linka[central].socket.addrs = ip4("10.0.0.128/24") -linkb[central].socket.addrs = ip4("10.0.1.128/24") - -for i in range(25): - suffix = str(i + 2) - linka[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") - linkb[snb[i]].socket.addrs = ip4(f"10.0.1.{suffix}/24") - -experiment(net) diff --git a/experiments/exp04-onechat2x25/network.png b/experiments/exp04-onechat2x25/network.png deleted file mode 100644 index 08907d0f054e50195dc9639abc79b0be1f3d7d01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11060 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 ThM9pE8X+n-H5Ciw&7b@iJ4Z{B diff --git a/experiments/exp04-onechat2x25/network.puml b/experiments/exp04-onechat2x25/network.puml deleted file mode 100644 index b171c2a5..00000000 --- a/experiments/exp04-onechat2x25/network.puml +++ /dev/null @@ -1,28 +0,0 @@ -@startuml -nwdiag { - network sna { - width = full - address = 10.0.0.0/24 - - n0 [address = 10.0.0.2]; - nx_a [shape = collections, description = n1...n23]; - n24 [address = 10.0.0.26]; - - router [address = 10.0.0.1]; - central [address = 10.0.0.128]; - } - - network snb { - address = 10.0.1.0/24 - - n25 [address = 10.0.1.2]; - nx_b [shape = collections, description = n26...n48]; - n49 [address = 10.0.1.26]; - - router [address = 10.0.1.1]; - central [address = 10.0.1.128]; - } -} -@enduml - - diff --git a/experiments/exp04-onechat2x25/playbook.yaml b/experiments/exp04-onechat2x25/playbook.yaml deleted file mode 100644 index ebd123f2..00000000 --- a/experiments/exp04-onechat2x25/playbook.yaml +++ /dev/null @@ -1,128 +0,0 @@ -- name: Prepare ansible host - hosts: 127.0.0.1 - connection: local - tasks: - - name: Generate node environments - changed_when: true - ansible.builtin.command: - cmd: ./generate-env.sh - - - name: Create metrics directory - ansible.builtin.file: - path: metrics - state: directory - mode: "0755" - -- name: Prepare fledger nodes - hosts: nodes - become: true - tasks: - - name: Copy binary - ansible.builtin.copy: - src: ~/fledger - dest: ~/ - mode: preserve - - - name: Copy service - ansible.builtin.copy: - src: fledger.service - dest: /etc/systemd/system/ - mode: preserve - - - name: Stop service - ansible.builtin.systemd_service: - name: fledger - state: stopped - daemon_reload: true - - - name: Copy environment - ansible.builtin.copy: - src: "env.systemd/{{ inventory_hostname }}.env" - dest: ~/env.systemd - mode: preserve - - - name: Remove old node and logs - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /root/flnode - - /var/log/fledger - - -- name: Prepare central node - hosts: central - become: true - tasks: - - name: Disable ip forwarding - ansible.posix.sysctl: - name: net.ipv4.ip_forward - value: 0 - sysctl_set: true - state: present - reload: true - - - name: Copy flsignal binary - ansible.builtin.copy: - src: ~/flsignal - dest: ~/ - mode: preserve - - - name: Copy flsignal service - ansible.builtin.copy: - src: flsignal.service - dest: /etc/systemd/system/ - mode: preserve - - - name: Stop flsignal - ansible.builtin.systemd_service: - name: flsignal - state: stopped - daemon_reload: true - - - name: Remove flsignal logs - ansible.builtin.file: - path: /var/log/flsignal - state: absent - - - name: Start flsignal - ansible.builtin.systemd_service: - name: flsignal - state: restarted - -- name: Run the simulation - hosts: nodes - become: true - timeout: 300 - tasks: - - name: Start fledger - ansible.builtin.systemd_service: - name: fledger - state: restarted - daemon_reload: true - - - name: Wait for success trigger - ansible.builtin.wait_for: - path: /var/log/fledger - search_regex: "RECV_CHAT_MSG TRIGGERED" - -# Must remain separate so it runs even if the simulation fails -- name: Download metrics - hosts: nodes - become: true - tasks: - - name: Download metrics - ansible.builtin.fetch: - src: /tmp/out.metrics - flat: true - dest: "metrics/{{ inventory_hostname }}.metrics" - -- name: Assemble metrics into one file - hosts: 127.0.0.1 - connection: local - tasks: - - name: Assemble metrics - ansible.builtin.assemble: - src: metrics - dest: assembled.metrics - mode: "644" diff --git a/experiments/exp05-onechat4x25/README.md b/experiments/exp05-onechat4x25/README.md deleted file mode 100644 index 5e970691..00000000 --- a/experiments/exp05-onechat4x25/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Exp5 onechat4x25 - -4 LANs of 25 nodes. - -LANs are organized in a square, i.e. there are four routers total -that link 2 lans each. -See plantuml diagram / network.png. - -Single central server for signaling diff --git a/experiments/exp05-onechat4x25/fledger.service b/experiments/exp05-onechat4x25/fledger.service deleted file mode 100644 index 18c75c58..00000000 --- a/experiments/exp05-onechat4x25/fledger.service +++ /dev/null @@ -1,26 +0,0 @@ -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=simple -ExecStart=/root/fledger \ - --config /root/flnode \ - --name ${FLEDGER_NODE_NAME} \ - --disable-turn-stun \ - --bootwait-max 60000 \ - --signal-url ws://${FLEDGER_CENTRAL_HOST}:8765 \ - -v \ - simulation \ - --print-new-messages \ - chat \ - --send-msg ${FLEDGER_SEND_MSG} \ - --recv-msg ${FLEDGER_RECV_MSG} -RemainAfterExit=yes -Restart=no -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp05-onechat4x25/flsignal.service b/experiments/exp05-onechat4x25/flsignal.service deleted file mode 100644 index c1ea0035..00000000 --- a/experiments/exp05-onechat4x25/flsignal.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Fledger signaling server -After=network.target - -[Service] -Type=simple -ExecStart=/root/flsignal -v -User=root -Restart=no -StandardOutput=append:/var/log/flsignal - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp05-onechat4x25/generate-env.sh b/experiments/exp05-onechat4x25/generate-env.sh deleted file mode 100755 index 5ef7e3ab..00000000 --- a/experiments/exp05-onechat4x25/generate-env.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -mkdir -p env.systemd - -amount=100 - -messages=() - -for i in $(seq $amount); do - message="$(openssl rand -hex 16)" - messages+=("$message") -done - -for i in $(seq 0 $((amount - 1))); do - j=$((("$i" + 2) % amount)) - nodename="n${i}" - envfile="env.systemd/${nodename}.env" - send_msg="${messages[$j]}" - recv_msg="${messages[$i]}" - - if test "$i" -lt 25; then - signalhost="10.0.0.128" - elif test "$i" -lt 50; then - signalhost="10.0.1.128" - elif test "$i" -lt 75; then - signalhost="10.0.2.128" - else - signalhost="10.0.3.128" - fi - - signalhost="10.0.128.128" - - { - echo "FLEDGER_CENTRAL_HOST=${signalhost}" - echo "FLEDGER_SEND_MSG=${send_msg}" - echo "FLEDGER_RECV_MSG=${recv_msg}" - echo "FLEDGER_NODE_NAME=${nodename}" - } >"$envfile" - - echo "[node $nodename]" - echo " <- ${recv_msg} [$i]" - echo " -> ${send_msg} [$j]" -done diff --git a/experiments/exp05-onechat4x25/hosts b/experiments/exp05-onechat4x25/hosts deleted file mode 100644 index 685648cd..00000000 --- a/experiments/exp05-onechat4x25/hosts +++ /dev/null @@ -1,104 +0,0 @@ -[nodes] -n0 -n1 -n2 -n3 -n4 -n5 -n6 -n7 -n8 -n9 -n10 -n11 -n12 -n13 -n14 -n15 -n16 -n17 -n18 -n19 -n20 -n21 -n22 -n23 -n24 -n25 -n26 -n27 -n28 -n29 -n30 -n31 -n32 -n33 -n34 -n35 -n36 -n37 -n38 -n39 -n40 -n41 -n42 -n43 -n44 -n45 -n46 -n47 -n48 -n49 -n50 -n51 -n52 -n53 -n54 -n55 -n56 -n57 -n58 -n59 -n60 -n61 -n62 -n63 -n64 -n65 -n66 -n67 -n68 -n69 -n70 -n71 -n72 -n73 -n74 -n75 -n76 -n77 -n78 -n79 -n80 -n81 -n82 -n83 -n84 -n85 -n86 -n87 -n88 -n89 -n90 -n91 -n92 -n93 -n94 -n95 -n96 -n97 -n98 -n99 - -[central] -central diff --git a/experiments/exp05-onechat4x25/model.py b/experiments/exp05-onechat4x25/model.py deleted file mode 100644 index bfadfade..00000000 --- a/experiments/exp05-onechat4x25/model.py +++ /dev/null @@ -1,56 +0,0 @@ -from mergexp import * - -net = Network('exp5', routing == static) - -def makeNode(i: int): - name = f"n{i}" - return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) - -lans = [ - [makeNode(i) for i in range(25)], - [makeNode(i) for i in range(25, 50)], - [makeNode(i) for i in range(50, 75)], - [makeNode(i) for i in range(75, 100)] -] - -router01 = net.node('router01', proc.cores>=1, memory.capacity>=mb(512)) -router12 = net.node('router12', proc.cores>=1, memory.capacity>=mb(512)) -router23 = net.node('router23', proc.cores>=1, memory.capacity>=mb(512)) - -central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) - -lans[0].extend([router01]) -lans[1].extend([router01, router12]) -lans[2].extend([router12, router23]) -lans[3].extend([router23]) - -links = [ - net.connect(lans[i], capacity==mbps(100), latency==ms(10)) - for i in range(len(lans)) -] - - -links[0][router01].socket.addrs = ip4(f"10.0.0.2/24") - -links[1][router01].socket.addrs = ip4(f"10.0.1.1/24") -links[1][router12].socket.addrs = ip4(f"10.0.1.2/24") - -links[2][router12].socket.addrs = ip4(f"10.0.2.1/24") -links[2][router23].socket.addrs = ip4(f"10.0.2.2/24") - -links[3][router23].socket.addrs = ip4(f"10.0.3.1/24") - -centralLan = [router12, central] -centralLink = net.connect(centralLan, capacity==mbps(100), latency==ms(10)) - -centralLink[central].socket.addrs = ip4(f"10.0.128.128/24") -centralLink[router12].socket.addrs = ip4(f"10.0.128.1/24") - -for i in range(len(links)): - lan = lans[i] - link = links[i] - for j in range(25): - n = str(j + 10) - link[lan[j]].socket.addrs = ip4(f"10.0.{i}.{n}/24") - -experiment(net) diff --git a/experiments/exp05-onechat4x25/network.png b/experiments/exp05-onechat4x25/network.png deleted file mode 100644 index b677c2fe0569cbe236bab146828b0d37e6b69724..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26144 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 QfSpJNPgg&ebxsLQ02nyT"$envfile" -done diff --git a/experiments/exp10-dht2x25/hosts b/experiments/exp10-dht2x25/hosts deleted file mode 100644 index aaf34ab2..00000000 --- a/experiments/exp10-dht2x25/hosts +++ /dev/null @@ -1,54 +0,0 @@ -[nodes] -n0 -n1 -n2 -n3 -n4 -n5 -n6 -n7 -n8 -n9 -n10 -n11 -n12 -n13 -n14 -n15 -n16 -n17 -n18 -n19 -n20 -n21 -n22 -n23 -n24 -n25 -n26 -n27 -n28 -n29 -n30 -n31 -n32 -n33 -n34 -n35 -n36 -n37 -n38 -n39 -n40 -n41 -n42 -n43 -n44 -n45 -n46 -n47 -n48 -n49 - -[central] -central diff --git a/experiments/exp10-dht2x25/model.py b/experiments/exp10-dht2x25/model.py deleted file mode 100644 index 2947124f..00000000 --- a/experiments/exp10-dht2x25/model.py +++ /dev/null @@ -1,32 +0,0 @@ -from mergexp import * - -net = Network('exp10', routing == static) - -def makeNode(i: int): - name = f"n{i}" - return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) - -sna = [makeNode(i) for i in range(25)] -snb = [makeNode(i) for i in range(25, 50)] - -router = net.node('router', proc.cores>=1, memory.capacity>=mb(512)) -central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) - -sna.extend([router, central]) -snb.extend([router, central]) - -linka = net.connect(sna, capacity==mbps(100), latency==ms(10)) -linkb = net.connect(snb, capacity==mbps(100), latency==ms(10)) - -linka[router].socket.addrs = ip4("10.0.0.1/24") -linkb[router].socket.addrs = ip4("10.0.1.1/24") - -linka[central].socket.addrs = ip4("10.0.0.128/24") -linkb[central].socket.addrs = ip4("10.0.1.128/24") - -for i in range(25): - suffix = str(i + 10) - linka[sna[i]].socket.addrs = ip4(f"10.0.0.{suffix}/24") - linkb[snb[i]].socket.addrs = ip4(f"10.0.1.{suffix}/24") - -experiment(net) diff --git a/experiments/exp10-dht2x25/network.png b/experiments/exp10-dht2x25/network.png deleted file mode 100644 index 08907d0f054e50195dc9639abc79b0be1f3d7d01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11060 zcmdsdcR1DW|Nqg+$_hyc$=+FI@3Ke8I9VwiWIM?wArfVsvNy#+93x~$$VeO`dt@AY zZ{J(>{=CQMbN#OC_q)E|KfXWL)#d8A&;5Gd1a1ZTh}1r@J4z%MShn}%-p9i2QLT3We5lq?-AUGBMAKDc7xaRu(?=Hx2Q&+qi` zo`W00{vqFeNBc*eZH!+<28Rk*b6JoC81!G7gx70+HO9|*hhB?;MR&}D}wsXfWlE8_H~giKq-z9sAa zd>!Gde^o@NA|y1{<-3K=y9!JJ&F<(A({C>y%5C0rlTUSYm)PA%s#oqBcp@R%nmHXx zPP4$R{6RCua5^^d+Nva(&0cEegxKsm=7%&~CBb<@ndXjBPQqcWu9s=;4eeae&(?g& z{Vvoy+rq@wh$HSt@;}$e^H{znuH5+D6>~C6%J-T6u-jIt5tBzNmO}eo18KjLaDv(mEOuYS$mt+*{q#SfzHQb&dUm5nWb-XW~ z+?AoR{mK2jwQ4(AN~FA{sJm_yrpWb*USm(-weG1ljWdm;HmigsJ)$?Gvd-iP5bsB3 zJwwxI@+OEoMr0<7nQhG!G3?&~me%krmNrEYeG%rqM2glD7#fZDFNek3ogCs2cP?RJqEt^>8lA51X4tRd#ID*ehvs zJUH@5SNW#i+1Oe_xPSFg{j~jSkB!HUqt$QPzwEgFc)VOyD^#3eU~V&CNA6DROzQ zx3W{Rsi)rYuJt`?XlNK92j81q%|hV7Q2eIV6+Q>sGcz+~Fw|IuvjPDjOiN3PjF|XK zTU&a(N!24cuxoYWYS^1MZ#Hzv%#V@MiyK!s)mv#{8NFBQb8lzNSt1ZyU)~6%fsHi> z(V)?2%_Q+x*;Vc<$8Fq&1hFnCv#LkDEG(G@g{HN89gd?uJ0_<)f63b>DK|H_!gFh3 z>3exrKxMbM)QubcawU(zL^zqtQ`%UV+{ce@Z!ghZe>k9CEr%T_y4Ps+js(^cOhR{E z*S=dEd|vd%wj}ZNz&4dftSwO3@R4a42}`{WO_82;?6?r&cBv^0R{3~sGCZ7EUsq2L z4gZ?n8qMC)(qf>mA2#H9E&k<8r{lxDXm+jUrY7|yaS=|=`J5DAiW|A_-o;C}FZY}I z@fHZ5l~yvaetA~SvQ8)I&XX>dfv@|6CDsR9*xA|HmKyWtj}O;ctUKaM9mi?}?l*r) zkumEX*8Ms>Yz&U&IW>Pzf)P03k8fHSycV;NrW8xIcZ(l{78W|Hs)kkcoZr1{k|amL z6N{sU&?l6RHdn%ySa(T3ULEgFQ+R_B5)xXeJvj!kXp6D4=h%NQn;e6#YsXw%dH?=> zKCAQY>cq>J=eO4f-roOUuXYSp*r+#{$7^OUgi4e=fdj~7E*8EZz zNJvPeoF~r`4ceY0to6t$AqjqWwD3;EQVarxP^QQZ?<|)RN>unK0(2#p#9DH z=;Y*1Q_ria^6aP*&#N*rGFPtnhf0f!gM;kTdwt!W7Trmx{3J`z{B0*!y){K*&wiQx z_vT2(F8TWUdZmcV_xPpAUx1&;Nb4<`#q6}U6{&Vbs-Py7B6QBCgk+5kM6%{pLIx#U(Q&U54 zXER#u@x9D`uwRN6Hzgxa=x>pDA`l33^DCUM+ZcU!?}2=H+SZY9gPNM!jzL31!)G#- z{i!Z8E{>E@+IwlJv_TDr!sn~W)vH(a3r$V*_1PR!WxOK_I=bcSaaZ<%L zB};kbLYbJDK(BlF5fo6V3 zh}(JMX>q)Fxq_y>sHmtq-U#J@Y9-~lX;@?)z%t}Cu|eTZMU~Md&jVdQ-?%(CR_!5l z{d$?#&N38_Sk!&lctlDvV#DO3l1b$c`{I&I&nx-bGwg@Zx6chOB!612ptQr}r(G9b z7P9(s-j5_*LKdrI>b>?UHH=;ny$0HLz8S1$k`I0>L^7>9P_`sUH(aOiv)D&Dxsl3}B3%zes zyw^j~Z$YP$(2g54XJKP2mRWWluftiRjt*6jmX=m!YBjY0c`)~(s;Eet7XJFREcW^x za02ppi^0=HUr!DhA}?%! zgm7S~_yNXm^Q)4S*Y;%ri>8JkBJ6cNGPDE!nt48~l#7o~lOrc5Cz4hqOoTFIj>6!a z%C-f8**$Q=4mPRv7Sbq*OG;HtOwdOP6ipdgT|aPea2gE8dGD5~ulL_~tNZ=bNA%Vg z7Akpa@m^zQB!(6dMuJ#wF0Q#p(%^=OfAYtH@|o4}IONm9M@T2@2Wor{KuZZ)2iu#G zo^B}QoU4&={dC=DEG;dWl_D}A1N_K}K^xMkH{nd%asF&sCh=7H^91 zsOpvj@PXt0M&Lf4gh61lH>|w1`D^`oUC@bdJyIy!zvb5+$D5Xx))-9UwL1~AvA#Yq zxK;0rv`@dmAA#}ec&@PiD+dn{V?H~aK`hqqC6+wUrf={LhGtjd5e<>;nvlq(2&r`NMsF^+L;~N z#%krZ3vGNU?wx1V4qqz`9^O|I?;AY{ZY*6ai}=`rfPp)nsSMQDQQaqm9g zEv9xh>GnR`<`X{2`KqtasJQi&wVGKSy%w9xy!Tw@TX&m(e%T+hCpXJ8n2Jzv9h3X7Q6s9t2`YKKURyaZq>Z;Yp03?64fy-5%m5gP`c1%?4kBm5XB|G#60a zZ{NNR{%Tt3W&r6rtZVc5o`x3Jx9Yb*8;>G6X=H>6@TE?9CFn4eqPFY)Q8u0+T%faqa z?*OXPFIS3D3co~7MO6xz$|*9PL7K${W78uuFGZA&0=jaqF&Jh1!3FRiP|YO~3m1q; z7$gTgP!gh=DyU6>Oa7oYEpAyW&OcnqpmOb20M~ zi2Y3)8x+^D`x^>|zRElre+)?-4XPcPm3hs~%xq#XjLjqAy8UOPo)N`4X3pUIsBeho z;iJuOTDmKSpIJ{Ir>}nz)mFQ|z&$Zjd%RT$8nTC%SM=F=&JdC4x-`s;0>9k4_Bx1P zdb3*r&?HH_B=J}zatCwQ@jcT4IGoAJNx(Zs5)&_{vB}7!MCckD^HdMNzu$6W%v+($ zb!W`)1Yn4u^Cl;NvTc5;^gZ(277IKaSFKnPWL@YJ|M>j{-HpzK8q zub5(N_IS$t^G;7sN8#l7=u=5N8^q-3SuVoMc#$d3_6`pE|$wm)COTcNYjqEV`1VTwPt?t}pcEy`UAbU}haI zyWu#h&i%UI)ayl?a5f;aIa7eH;b+`>;W-gRy~r?(vs|Sf>RBpn*%rg35Zq1oc&pE# zD^W~AjE~r*)E@j-GEr5`OM;aprTZUf!*EOl`l$= zE}O3|WSF8#uwOJ#?U4%Nsev4pu~zaWRvptW7sj=*>uJahir{ja>ke4HtGByTd!j*>-_S)>TvF2N-=J#d?bLF5 z5SQ(annEd2u+ntWVp@EkSCP`~{@ryB)~)N2lIza^bs&*dk+qm^?*Wi7oL(YMWDbM+ zKKjvXP)%%KIV>#{qf)=ghEGcin$>peG@7Q$e`1p9bALFUKdERO{X*xKGEts#1=U2*q0 z+47|wlkBJWQI3PfmUaF(lRsmuQN&Jm_P~6HE1c1FE8lX+G*~fsuzouBQogl*q?xY@ zx7+ERto7LVVcnH{WQkAnD^{Dqo(ktFKq^{^(kFOwe8+l0?>t;{1g$_f)lbT=Q#VmbxYPL87t_5KWET{A)xKp1*IyY~sVDoXL z3f<>>W~x?dxZi%%lEvOi*TkY7vY1#t=ynX1+Rgtkw*DFPgdw(*`B1pkp`I1~75V1P z8vr9ndb85tBg+bep~pQYpa3bli)MvT;Ao;XeQds$EuC^F_1I~iN=l-%gXJoIDXEW{ z+a7s<={>>B*Ka=Oui<#R#6ME0{7QZb@aS{6);w(EHSIA@`3TS9%w>Yee&JVzX0^-e zW&?92#LBeVz736yYPOML_Cvz(5#?SRFkQ0Nb8XKDL<9t)Zgbj6^bbyJj6SUYO>jD* zii~yg*;oJ+ zKkA%KOI@8TY&L4%c%Q?l4seAb_T? zNahGWdhKKj%VTd%hGSw=m3jdO4LD-Ih7SV5Loc&z*^ang%0-BQ{_ucxk6)oj^}2+_ zT^$u7Z+#VN84aB6sf$P_P|17qwxWi{Xxl%3u6VRWo9A4^KS}&7jk#O*?bHeO;jh|W zPmhv_l#a5H#cG}bUG{)aw}A%b8gu}}EiB}}shu7I7I_9u3c9+k?U6mXZjPqOs8hGt z{qD+u(`0R;)(QLtDz0T=J_&cnR{f*DYi+h(LOWbJy>UO=vv;~yb1DM*8#~M*$BdaZL}o)H)Hm%`rM#pl6vvzX>hP=-mv*hTWn8x0eO#*O<0-Ac_*1uT~C$- zF!yMEBQoh|2$1u7>vF!j1$VKWs(&J@Ho{30LI6mBQQou~*1PH;euy?S<<>Wv|CPi2 z`;35y@sdelpuM-Z*Yu+&E$T&P3oJQG@`f{^QG= zZ>YQF3{svvhDH9R00fbg(p)pI3#Sos7^~@67{P(z*Y#BIPCf^4==k`!q5a8|ClT}# zrXO6gii=%<+yp4Xg&P-@RzA!!sOY(Huq*6mRJO0fH-2=)?z4)i`T8CMg>D%%R3LtW zdbKiAh2$zJE(X)IwYR^O^x5B>o_?@Kt_MXy&24_O>%|g~ch)IB`W!i#nN3YihdpQq zGy;o>%AMBM4Bk`%O#L=M3t7HmbL%mjx@8Xs<%6oLtLy6OfV`^nH{J}%C$$M0WET){ z2Ev~F9?+MjS|U%fOh$4eBO_UC^Sn3+N#*x53P8v}*uA{j?!WbDa8D2m>LVzt*>CL_(h=b2`bi3cuqxTL;vW zHulA3u_)38+uXwjB7TAy06~h2fgk}fq<`;o?%cUEP^g-!YObX7WD}srx6^*p)!g#( z8#j%VeL3)%8bMf0T+2vBENP{&si`Kn&--topgF3iNSkm(V+M~JHGm+VAE8*GqM`z% z7Equfo;}OW&(DniO+Kg0FS4R^^)5$UZj<}$)Qd2B7N$Ufl9+Rmmp^UtXO;!;Hxs|B z26Zn}B^tETW^hq}aG9UallJ~JR0t;;eF2k0oXz!pgyTTtva|A&!1Iox-z+_pkFm;J zmEK?resV{;pBei3g_Yd@bXLkM^zDCoPQ5bxffx2>R8yvxf2sISnZs?4e*K1&U$^Gt*pl_KW)164j678%*UVAlnK zbTY}i)OshqinFISSP*L&1(D3!?*0r^@ z^%It5QQH!C+2BAQN(ze36d6D82jI56)y)Cm|CFgr;sI?DB>T}mw(wJI<-Hqe`cM^F zJc#4pje`6C$tb8k&K-dY#%yV&bB0jY99+0y6ETm%9P#llZBo>T{2q-GLW;Fp2P$Yv?e2}>N z^5N0WSP3#BgLn;xhIIQ6uYkO@wKd<_$jAs(F+drhC_18_GXO0dB=hs|@CRwqKKq$D zIX}Nz1v3~zKGlR-Zz+QNEGrA4^l@|B1PtKqwVxPG_T||Jkkw#a*cfmLcGsqiI7CHv zKfhvL%$nk8EWpHW|I;cM`-@fZ0{7=8i)L+s=J8b=#$oW&;dydI_}nvtxC&NY#ZPs3 zbTnwzRpGJ0wv-SP)3kk#jBICXYspXzv@3w`xxGg+I?biO{|qBU7gktMKuS!^f~&jN zNG7w`Uzno(0U&cwqFQceLB$7dh=^>3vL~O1#r?}Ba3|A+hStpSk&}}H-;Ix(oAO3C z{vU_*+aQ=*Cm|%9A1cKH@5#Q@uQAtos`+4hDenf?73tI|pFeB@UX?*0Jn$-B2KxFk zlU-fNwdN-*SD^kkE|l`+q^Doy=a0nn=l|H>!fM`{5gjd;^WS@@K=Bf%j*$;euUzL{ z#zgoTGEN3Jk$T;Aodt&ma)^UE`2vQA5Ysf6W@l$_FZ6MP{oU#}qb_|%X**b)#quDU zKIFEZp0IUiqQgk#*4)?o^-pknoB}^x*3lK%=V9seSIh7Ndo?g{+StfIF9VGou>+Bn z>h>hzui3TG(~GICtz9h!!Z|nvg978X`T3Wny?5&a@r~4+=aRja$wM9%Pd#hx>KX$M z9?&VkbNXuiPC&O&y(_G)o{B*H^bZOmj)l4;OWfvr-o4|pv4adlVTBXDZ*{Y>vL4DW z11>#iokGd@fqhIN5B1%6yA^ z2XANWtZX0zc0%KM)rwM3K;V@RavIdirdoV%(!Jf?X9{N~rv_Nr*+Y`6h=_=?0)j(< z8mlYvbLATuCwsFgGJDg}pqB%4End8c(gn}zEwtp<#pPbNbUnClYoHWyf32{h&PI#8@G-=bt{Ej z>oZ6?kV?87jw6*KTwJG0`w-fOnUqlU^}uh%8>$K=q1mt*Y5i)=1gIm;5!gq~gzFj{ zrcqj(DU@DSr&R{!CaKvTLrJy&vG3Oy(l(J?Ux zY~VmrR`2bomoNSM#JkVKkE~fJ1;MU!aOAqLjDlMp8ygGy61b=h_>(+}FyzzemV3A3 zD?8#vh$`9viZo2Xd$EUhaCO);I+djt{(~lM3IUpT0RgJ$DdLU)Zb+j1Gn$UIE*W|y zI`O}#%l9qCB9v;=)8!8lk0Ed%*D{pQcCh};R6OvXKXS|;(--(h9&}fFUQ4l8J|hU( zg~ANTew2`$RtP^?>_Yv2%#J+#jltm46q89sZ2eIN@c)}8?_V{zBf0*WxreLY<6x;g zYvmU6vZ%plx;8w)n0%^hhrsY@&!GCv$Gx``i&7B=0;_`ln1Em~ z(6QU2~FFz;+e^*;aqsxS6<`I2Dn~1QHpq)4EIL z9L${x$`6Ip!wG&rDC*xIl=<%u;)IXf`G1;94}JtJZh#}>1uf4R{Qw3^{&9k1%^ToA z1ED86D$3Ue6?Xd0!oUDPn78*1_ck_WyK=sP-pm2G@sQqGG^zi_AM52bDD~Z590bj$ z8mO`<0DNRbO4HKQ^Y-RsXYU^z6dIKTXZjuPu2o8W_yT+`A8+qD_}<H3;v-B{w=sQ^x zQBlAM6Et2c%ntwmX4Q%!P%D1C+`fAd)zMmtu^~3X@f1$dZvrCHH5JwMY8h;(f&Q z;%`_*j?W$~)rVr}jqxV&$vsuv>j4U3 z0zDv09|ikrqA_d=IQr?Hq>b+7qt93t&7jK5-90_UHC*DqL|uQhmY8|jPKudXfg{@f z6R)d_%l^(vm$AC1?O4snR2D)05p)EDR2%L0Ps{b?_TR62Zo<=ay7yzb4Z!OPTgL}0 zx>LY3{F;=JoBNDj@eDi6pptl+>?(jbz!|(RQD)cQ={)*7N5E!eQRPDJj$XWYF)A5| zS;CJN4FM;;=e%&KOQr2ShTpj2O3Wu%Etq<|K2Sv5BB!Nj%|egl_+uij{KObF*Yx5 zVU1g&KQw`=aiAvxL^L3a-cRuXU_Cj=(8zdR5*HU&=5RGIP^%C3F{biRPVw;DZBJw7 zm6N?LKP!<(0lz9|ZbH}kj6HzRIhsj;<1#!R$AOJ9NPAaw-d>y#Q?bcKG)@>{Et(h` zUQYTQd>`xCx(41t^IW;xecCHO27Atys6DW>>^pcR-sl{;A2gl?#jGNk7;DFvI?nNw8V*B$;Ta8XrZpb7k1 zbA8?3qL_*tw!q@ARo&~qb*jv*8TY+*v*U&~k_+^LR{waA#TT^8&cAgJRbuoq@jn7* z#e=!y{i~%wJFMrGK6rv{3?=vL9PM5Mb9|xrxvGD@es`({{9nAS_y76uH9yg-3ZkDL ThM9pE8X+n-H5Ciw&7b@iJ4Z{B diff --git a/experiments/exp10-dht2x25/network.puml b/experiments/exp10-dht2x25/network.puml deleted file mode 100644 index b171c2a5..00000000 --- a/experiments/exp10-dht2x25/network.puml +++ /dev/null @@ -1,28 +0,0 @@ -@startuml -nwdiag { - network sna { - width = full - address = 10.0.0.0/24 - - n0 [address = 10.0.0.2]; - nx_a [shape = collections, description = n1...n23]; - n24 [address = 10.0.0.26]; - - router [address = 10.0.0.1]; - central [address = 10.0.0.128]; - } - - network snb { - address = 10.0.1.0/24 - - n25 [address = 10.0.1.2]; - nx_b [shape = collections, description = n26...n48]; - n49 [address = 10.0.1.26]; - - router [address = 10.0.1.1]; - central [address = 10.0.1.128]; - } -} -@enduml - - diff --git a/experiments/exp10-dht2x25/playbook.yaml b/experiments/exp10-dht2x25/playbook.yaml deleted file mode 100644 index 9530d52a..00000000 --- a/experiments/exp10-dht2x25/playbook.yaml +++ /dev/null @@ -1,143 +0,0 @@ -- name: Prepare ansible host - hosts: 127.0.0.1 - connection: local - tasks: - - name: Generate node environments - changed_when: true - ansible.builtin.command: - cmd: ./generate-env.sh - - - name: Create metrics directory - ansible.builtin.file: - path: metrics - state: directory - mode: "0755" - -- name: Prepare fledger nodes - hosts: nodes - become: true - tasks: - - name: Copy binary - ansible.builtin.copy: - src: ~/fledger - dest: ~/ - mode: preserve - - - name: Copy service - ansible.builtin.copy: - src: fledger.service - dest: /etc/systemd/system/ - mode: preserve - - - name: Stop service - ansible.builtin.systemd_service: - name: fledger - state: stopped - daemon_reload: true - - - name: Copy environment - ansible.builtin.copy: - src: "env.systemd/{{ inventory_hostname }}.env" - dest: ~/env.systemd - mode: preserve - - - name: Remove old node and logs - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /root/flnode - - /var/log/fledger - - -- name: Prepare central node - hosts: central - become: true - tasks: - - name: Disable ip forwarding - ansible.posix.sysctl: - name: net.ipv4.ip_forward - value: 0 - sysctl_set: true - state: present - reload: true - - - name: Copy binaries - ansible.builtin.copy: - src: "{{ item }}" - dest: ~/ - mode: preserve - loop: - - ~/fledger - - ~/flsignal - - - name: Copy services - ansible.builtin.copy: - src: "{{ item }}" - dest: /etc/systemd/system/ - mode: preserve - loop: - - flsignal.service - - flrealm.service - - - name: Stop services - ansible.builtin.systemd_service: - name: "{{ item }}" - state: stopped - daemon_reload: true - loop: - - flsignal - - flrealm - - - name: Remove logs - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /var/log/flsignal - - /var/log/flrealm - - - name: Start services - ansible.builtin.systemd_service: - name: "{{ item }}" - state: restarted - loop: - - flsignal - - flrealm - -- name: Run the simulation - hosts: nodes - become: true - timeout: 300 - tasks: - - name: Start fledger - ansible.builtin.systemd_service: - name: fledger - state: restarted - daemon_reload: true - - - name: Wait for success trigger - ansible.builtin.wait_for: - path: /var/log/fledger - search_regex: "SIMULATION END" - -# Must remain separate so it runs even if the simulation fails -- name: Download metrics - hosts: nodes - become: true - tasks: - - name: Download metrics - ansible.builtin.fetch: - src: /tmp/out.metrics - flat: true - dest: "metrics/{{ inventory_hostname }}.metrics" - -- name: Assemble metrics into one file - hosts: 127.0.0.1 - connection: local - tasks: - - name: Assemble metrics - ansible.builtin.assemble: - src: metrics - dest: assembled.metrics - mode: "644" diff --git a/experiments/exp11-dht4x25/README.md b/experiments/exp11-dht4x25/README.md deleted file mode 100644 index 53470347..00000000 --- a/experiments/exp11-dht4x25/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Exp11 dht4x25 - -Two LANs of 25 nodes separated by a router. - -Single central node with: - -- The signaling server -- A node that creates a realm in that signaling server - -The 50 nodes then simply connect and join the realm. diff --git a/experiments/exp11-dht4x25/fledger.service b/experiments/exp11-dht4x25/fledger.service deleted file mode 100644 index 2eaf2dff..00000000 --- a/experiments/exp11-dht4x25/fledger.service +++ /dev/null @@ -1,23 +0,0 @@ - -[Unit] -Description=Fledger node -After=network.target - -[Service] -Type=simple -ExecStart=/root/fledger \ - --config /root/flnode \ - --name ${FLEDGER_NODE_NAME} \ - --disable-turn-stun \ - --bootwait-max 15000 \ - --signal-url ws://${FLEDGER_CENTRAL_HOST}:8765 \ - -v \ - simulation dht-join-realm -RemainAfterExit=yes -Restart=no -User=root -EnvironmentFile=/root/env.systemd -StandardOutput=append:/var/log/fledger - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp11-dht4x25/flrealm.service b/experiments/exp11-dht4x25/flrealm.service deleted file mode 100644 index 6d978857..00000000 --- a/experiments/exp11-dht4x25/flrealm.service +++ /dev/null @@ -1,23 +0,0 @@ - -[Unit] -Description=Fledger node to create a realm -After=network.target - -[Service] -Type=simple -ExecStart=/root/fledger \ - --config /root/flrealm \ - --name flrealm \ - --disable-turn-stun \ - --signal-url ws://127.0.0.1:8765 \ - -v \ - realm create \ - 65535 \ - 65535 -RemainAfterExit=yes -Restart=no -User=root -StandardOutput=append:/var/log/flrealm - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp11-dht4x25/flsignal.service b/experiments/exp11-dht4x25/flsignal.service deleted file mode 100644 index c1ea0035..00000000 --- a/experiments/exp11-dht4x25/flsignal.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Fledger signaling server -After=network.target - -[Service] -Type=simple -ExecStart=/root/flsignal -v -User=root -Restart=no -StandardOutput=append:/var/log/flsignal - -[Install] -WantedBy=multi-user.target diff --git a/experiments/exp11-dht4x25/generate-env.sh b/experiments/exp11-dht4x25/generate-env.sh deleted file mode 100755 index 5837fc87..00000000 --- a/experiments/exp11-dht4x25/generate-env.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -mkdir -p env.systemd - -amount=100 - -for i in $(seq 0 $((amount - 1))); do - nodename="n${i}" - envfile="env.systemd/${nodename}.env" - - signalhost="10.0.128.128" - - { - echo "FLEDGER_CENTRAL_HOST=${signalhost}" - echo "FLEDGER_NODE_NAME=${nodename}" - } >"$envfile" -done diff --git a/experiments/exp11-dht4x25/hosts b/experiments/exp11-dht4x25/hosts deleted file mode 100644 index 685648cd..00000000 --- a/experiments/exp11-dht4x25/hosts +++ /dev/null @@ -1,104 +0,0 @@ -[nodes] -n0 -n1 -n2 -n3 -n4 -n5 -n6 -n7 -n8 -n9 -n10 -n11 -n12 -n13 -n14 -n15 -n16 -n17 -n18 -n19 -n20 -n21 -n22 -n23 -n24 -n25 -n26 -n27 -n28 -n29 -n30 -n31 -n32 -n33 -n34 -n35 -n36 -n37 -n38 -n39 -n40 -n41 -n42 -n43 -n44 -n45 -n46 -n47 -n48 -n49 -n50 -n51 -n52 -n53 -n54 -n55 -n56 -n57 -n58 -n59 -n60 -n61 -n62 -n63 -n64 -n65 -n66 -n67 -n68 -n69 -n70 -n71 -n72 -n73 -n74 -n75 -n76 -n77 -n78 -n79 -n80 -n81 -n82 -n83 -n84 -n85 -n86 -n87 -n88 -n89 -n90 -n91 -n92 -n93 -n94 -n95 -n96 -n97 -n98 -n99 - -[central] -central diff --git a/experiments/exp11-dht4x25/model.py b/experiments/exp11-dht4x25/model.py deleted file mode 100644 index b5ab8e3a..00000000 --- a/experiments/exp11-dht4x25/model.py +++ /dev/null @@ -1,58 +0,0 @@ -from mergexp import * - -net = Network('exp11', routing == static) - -def makeNode(i: int): - name = f"n{i}" - return net.node(name, proc.cores>=1, memory.capacity>=mb(512)) - -lans = [ - [makeNode(i) for i in range(25)], - [makeNode(i) for i in range(25, 50)], - [makeNode(i) for i in range(50, 75)], - [makeNode(i) for i in range(75, 100)] -] - -router01 = net.node('router01', proc.cores>=1, memory.capacity>=mb(512)) -router12 = net.node('router12', proc.cores>=1, memory.capacity>=mb(512)) -router23 = net.node('router23', proc.cores>=1, memory.capacity>=mb(512)) - -central = net.node('central', proc.cores>=2, memory.capacity>=mb(512)) - -lans[0].extend([router01]) -lans[1].extend([router01, router12]) -lans[2].extend([router12, router23]) -lans[3].extend([router23]) - -links = [ - #net.connect(lans[i], capacity==mbps(100), latency==ms(10)) - net.connect(lans[i]) # atm capacity and latency break sphere - for i in range(len(lans)) -] - - -links[0][router01].socket.addrs = ip4(f"10.0.0.2/24") - -links[1][router01].socket.addrs = ip4(f"10.0.1.1/24") -links[1][router12].socket.addrs = ip4(f"10.0.1.2/24") - -links[2][router12].socket.addrs = ip4(f"10.0.2.1/24") -links[2][router23].socket.addrs = ip4(f"10.0.2.2/24") - -links[3][router23].socket.addrs = ip4(f"10.0.3.1/24") - -centralLan = [router12, central] -#centralLink = net.connect(centralLan, capacity==mbps(100), latency==ms(10)) -centralLink = net.connect(centralLan) - -centralLink[central].socket.addrs = ip4(f"10.0.128.128/24") -centralLink[router12].socket.addrs = ip4(f"10.0.128.1/24") - -for i in range(len(links)): - lan = lans[i] - link = links[i] - for j in range(25): - n = str(j + 10) - link[lan[j]].socket.addrs = ip4(f"10.0.{i}.{n}/24") - -experiment(net) diff --git a/experiments/exp11-dht4x25/network.png b/experiments/exp11-dht4x25/network.png deleted file mode 100644 index 08907d0f054e50195dc9639abc79b0be1f3d7d01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11060 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 ThM9pE8X+n-H5Ciw&7b@iJ4Z{B diff --git a/experiments/exp11-dht4x25/network.puml b/experiments/exp11-dht4x25/network.puml deleted file mode 100644 index b171c2a5..00000000 --- a/experiments/exp11-dht4x25/network.puml +++ /dev/null @@ -1,28 +0,0 @@ -@startuml -nwdiag { - network sna { - width = full - address = 10.0.0.0/24 - - n0 [address = 10.0.0.2]; - nx_a [shape = collections, description = n1...n23]; - n24 [address = 10.0.0.26]; - - router [address = 10.0.0.1]; - central [address = 10.0.0.128]; - } - - network snb { - address = 10.0.1.0/24 - - n25 [address = 10.0.1.2]; - nx_b [shape = collections, description = n26...n48]; - n49 [address = 10.0.1.26]; - - router [address = 10.0.1.1]; - central [address = 10.0.1.128]; - } -} -@enduml - - diff --git a/experiments/exp11-dht4x25/playbook.yaml b/experiments/exp11-dht4x25/playbook.yaml deleted file mode 100644 index 9530d52a..00000000 --- a/experiments/exp11-dht4x25/playbook.yaml +++ /dev/null @@ -1,143 +0,0 @@ -- name: Prepare ansible host - hosts: 127.0.0.1 - connection: local - tasks: - - name: Generate node environments - changed_when: true - ansible.builtin.command: - cmd: ./generate-env.sh - - - name: Create metrics directory - ansible.builtin.file: - path: metrics - state: directory - mode: "0755" - -- name: Prepare fledger nodes - hosts: nodes - become: true - tasks: - - name: Copy binary - ansible.builtin.copy: - src: ~/fledger - dest: ~/ - mode: preserve - - - name: Copy service - ansible.builtin.copy: - src: fledger.service - dest: /etc/systemd/system/ - mode: preserve - - - name: Stop service - ansible.builtin.systemd_service: - name: fledger - state: stopped - daemon_reload: true - - - name: Copy environment - ansible.builtin.copy: - src: "env.systemd/{{ inventory_hostname }}.env" - dest: ~/env.systemd - mode: preserve - - - name: Remove old node and logs - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /root/flnode - - /var/log/fledger - - -- name: Prepare central node - hosts: central - become: true - tasks: - - name: Disable ip forwarding - ansible.posix.sysctl: - name: net.ipv4.ip_forward - value: 0 - sysctl_set: true - state: present - reload: true - - - name: Copy binaries - ansible.builtin.copy: - src: "{{ item }}" - dest: ~/ - mode: preserve - loop: - - ~/fledger - - ~/flsignal - - - name: Copy services - ansible.builtin.copy: - src: "{{ item }}" - dest: /etc/systemd/system/ - mode: preserve - loop: - - flsignal.service - - flrealm.service - - - name: Stop services - ansible.builtin.systemd_service: - name: "{{ item }}" - state: stopped - daemon_reload: true - loop: - - flsignal - - flrealm - - - name: Remove logs - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /var/log/flsignal - - /var/log/flrealm - - - name: Start services - ansible.builtin.systemd_service: - name: "{{ item }}" - state: restarted - loop: - - flsignal - - flrealm - -- name: Run the simulation - hosts: nodes - become: true - timeout: 300 - tasks: - - name: Start fledger - ansible.builtin.systemd_service: - name: fledger - state: restarted - daemon_reload: true - - - name: Wait for success trigger - ansible.builtin.wait_for: - path: /var/log/fledger - search_regex: "SIMULATION END" - -# Must remain separate so it runs even if the simulation fails -- name: Download metrics - hosts: nodes - become: true - tasks: - - name: Download metrics - ansible.builtin.fetch: - src: /tmp/out.metrics - flat: true - dest: "metrics/{{ inventory_hostname }}.metrics" - -- name: Assemble metrics into one file - hosts: 127.0.0.1 - connection: local - tasks: - - name: Assemble metrics - ansible.builtin.assemble: - src: metrics - dest: assembled.metrics - mode: "644" From c26579b5919348f8890f6812c825a3bbd61338fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 28 Apr 2025 07:44:23 +0200 Subject: [PATCH 24/43] feat: some metrics --- Cargo.lock | 1156 +++++++++++++++++- cli/fledger/src/main.rs | 24 +- cli/fledger/src/metrics.rs | 4 +- cli/flsignal/Cargo.toml | 9 +- cli/flsignal/src/main.rs | 17 +- flarch/Cargo.toml | 43 +- flarch/src/broker.rs | 11 + flarch/src/web_rtc/libc/web_socket_client.rs | 3 + flarch/src/web_rtc/libc/web_socket_server.rs | 3 + flmodules/Cargo.toml | 5 +- flmodules/src/network/broker.rs | 13 + 11 files changed, 1231 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f84cd003..ec7ba8b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -141,6 +153,15 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + [[package]] name = "asn1-rs" version = "0.6.2" @@ -180,6 +201,149 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-compression" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 0.38.44", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.4.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-object-pool" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333c456b97c3f2d50604e8b2624253b7f787208cb72eb75e64b0ad11b221652c" +dependencies = [ + "async-std", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel 2.3.1", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.0", + "futures-lite", + "rustix 0.38.44", + "tracing", +] + [[package]] name = "async-recursion" version = "1.1.1" @@ -191,6 +355,58 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.44", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-std" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.88" @@ -235,6 +451,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -247,6 +469,17 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +[[package]] +name = "basic-cookies" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" +dependencies = [ + "lalrpop", + "lalrpop-util", + "regex", +] + [[package]] name = "bimap" version = "0.6.3" @@ -262,6 +495,21 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -295,6 +543,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "btparse" version = "0.2.0" @@ -445,6 +706,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -500,6 +770,36 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -653,6 +953,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc 0.2.172", + "redox_users", + "winapi", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -742,6 +1063,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enum-display" version = "0.1.4" @@ -803,6 +1139,49 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc 0.2.172", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.0", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "ff" version = "0.13.1" @@ -819,6 +1198,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flarch" version = "0.9.2" @@ -834,6 +1219,7 @@ dependencies = [ "futures", "js-sys", "log", + "metrics", "rand 0.8.5", "regex", "rmp-serde", @@ -854,13 +1240,23 @@ dependencies = [ "webrtc", ] +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flcrypto" version = "0.9.2" dependencies = [ "anyhow", "async-recursion", - "base64", + "base64 0.22.1", "bytes", "ed25519-dalek", "enum_dispatch", @@ -892,6 +1288,8 @@ dependencies = [ "flmodules", "flnode", "log", + "metrics", + "metrics-exporter-influx", "thiserror 2.0.12", "tokio", "webrtc-util", @@ -925,8 +1323,9 @@ dependencies = [ "flmodules", "futures", "getrandom 0.2.16", - "itertools", + "itertools 0.14.0", "log", + "metrics", "names", "num-bigint", "rand 0.8.5", @@ -958,7 +1357,7 @@ dependencies = [ "flmodules", "futures", "getrandom 0.2.16", - "itertools", + "itertools 0.14.0", "log", "names", "rand 0.8.5", @@ -982,6 +1381,8 @@ dependencies = [ "flarch", "flmodules", "log", + "metrics", + "metrics-exporter-influx", "thiserror 2.0.12", "tokio", ] @@ -1049,6 +1450,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -1144,6 +1558,18 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "group" version = "0.13.0" @@ -1161,6 +1587,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.15.3" @@ -1173,6 +1608,18 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -1197,6 +1644,17 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.3.1" @@ -1208,6 +1666,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1215,7 +1684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.3.1", ] [[package]] @@ -1226,8 +1695,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "pin-project-lite", ] @@ -1237,6 +1706,63 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "httpmock" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ec9586ee0910472dec1a1f0f8acf52f0fdde93aea74d70d4a3107b4be0fd5b" +dependencies = [ + "assert-json-diff", + "async-object-pool", + "async-std", + "async-trait", + "base64 0.21.7", + "basic-cookies", + "crossbeam-utils", + "form_urlencoded", + "futures-util", + "hyper 0.14.32", + "lazy_static", + "levenshtein", + "log", + "regex", + "serde", + "serde_json", + "serde_regex", + "similar", + "tokio", + "url", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.6.0" @@ -1246,8 +1772,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -1263,8 +1789,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http", - "hyper", + "http 1.3.1", + "hyper 1.6.0", "hyper-util", "rustls", "rustls-pki-types", @@ -1283,9 +1809,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.6.0", "libc 0.2.172", "pin-project-lite", "socket2", @@ -1495,6 +2021,24 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -1544,12 +2088,58 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools 0.11.0", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax 0.8.5", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata 0.4.9", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "levenshtein" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" + [[package]] name = "libc" version = "0.2.172" @@ -1569,12 +2159,34 @@ dependencies = [ "tokio", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc 0.2.172", +] + [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.8.0" @@ -1596,6 +2208,9 @@ name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "value-bag", +] [[package]] name = "lru-slab" @@ -1603,6 +2218,24 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc 0.2.172", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "md-5" version = "0.10.6" @@ -1628,6 +2261,70 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metrics" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" +dependencies = [ + "ahash", + "metrics-macros", + "portable-atomic", +] + +[[package]] +name = "metrics-exporter-influx" +version = "0.2.2" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "http 0.2.12", + "httpmock", + "indexmap 1.9.3", + "itertools 0.13.0", + "metrics", + "metrics-util", + "quanta 0.12.5", + "reqwest", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-retry", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "metrics-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "metrics-util" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de2ed6e491ed114b40b732e4d1659a9d53992ebd87490c44a6ffe23739d973e" +dependencies = [ + "aho-corasick", + "crossbeam-epoch", + "crossbeam-utils", + "hashbrown 0.13.1", + "indexmap 1.9.3", + "metrics", + "num_cpus", + "ordered-float", + "quanta 0.11.1", + "radix_trie", + "sketches-ddsketch", +] + [[package]] name = "mime" version = "0.3.17" @@ -1679,6 +2376,21 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nix" version = "0.26.4" @@ -1702,6 +2414,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1737,6 +2459,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc 0.2.172", +] + [[package]] name = "object" version = "0.36.7" @@ -1773,6 +2505,21 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p256" version = "0.13.2" @@ -1797,6 +2544,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1832,7 +2585,7 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ - "base64", + "base64 0.22.1", "serde", ] @@ -1851,6 +2604,51 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.9.0", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1876,6 +2674,17 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -1886,6 +2695,21 @@ dependencies = [ "spki", ] +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.44", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "polyval" version = "0.6.2" @@ -1937,6 +2761,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "primeorder" version = "0.13.6" @@ -1955,6 +2785,37 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quanta" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +dependencies = [ + "crossbeam-utils", + "libc 0.2.172", + "mach2", + "once_cell", + "raw-cpuid 10.7.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + +[[package]] +name = "quanta" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" +dependencies = [ + "crossbeam-utils", + "libc 0.2.172", + "once_cell", + "raw-cpuid 11.5.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quinn" version = "0.11.8" @@ -2025,6 +2886,16 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -2084,6 +2955,24 @@ dependencies = [ "getrandom 0.3.3", ] +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "raw-cpuid" +version = "11.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "rcgen" version = "0.13.2" @@ -2107,6 +2996,17 @@ dependencies = [ "bitflags 2.9.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "regex" version = "1.11.1" @@ -2115,8 +3015,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2127,9 +3036,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -2142,14 +3057,15 @@ version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ - "base64", + "async-compression", + "base64 0.22.1", "bytes", "futures-core", "futures-util", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.6.0", "hyper-rustls", "hyper-util", "ipnet", @@ -2283,6 +3199,32 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc 0.2.172", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc 0.2.172", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + [[package]] name = "rustls" version = "0.23.27" @@ -2473,6 +3415,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" +dependencies = [ + "regex", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2491,7 +3443,7 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -2549,6 +3501,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shared" version = "0.9.1" @@ -2603,6 +3564,24 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "sketches-ddsketch" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85636c14b73d81f541e525f585c0a2109e6744e1565b5c1668e31c70c10ed65c" + [[package]] name = "slab" version = "0.4.9" @@ -2653,6 +3632,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.11.1" @@ -2665,7 +3656,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea256fb46a13f9204e9dee9982997b2c3097db175a9fddaa8350310d03c4d5a3" dependencies = [ - "base64", + "base64 0.22.1", "crc", "lazy_static", "md-5", @@ -2735,6 +3726,30 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.7", + "windows-sys 0.59.0", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2775,6 +3790,16 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.41" @@ -2806,6 +3831,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -2860,6 +3894,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand 0.8.5", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.2" @@ -2943,10 +3988,23 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "tracing-core" version = "0.1.33" @@ -2954,6 +4012,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -2970,7 +4058,7 @@ checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ "bytes", "data-encoding", - "http", + "http 1.3.1", "httparse", "log", "rand 0.9.1", @@ -2988,7 +4076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0044fdae001dd8a1e247ea6289abf12f4fcea1331a2364da512f9cd680bbd8cb" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "futures", "log", "md-5", @@ -3020,6 +4108,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" @@ -3074,6 +4168,18 @@ dependencies = [ "getrandom 0.3.3", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + [[package]] name = "version_check" version = "0.9.5" diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 8e27f393..c768792e 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -1,5 +1,6 @@ use clap::{Parser, Subcommand}; +use ::metrics::absolute_counter; use flarch::{ data_storage::{DataStorage, DataStorageFile}, random, @@ -12,6 +13,7 @@ use flmodules::{ network::{broker::NetworkIn, network_start, signal::SIGNAL_VERSION}, }; use flnode::{node::Node, version::VERSION_STRING}; +use metrics::Metrics; use page::{Page, PageCommands}; use realm::{RealmCommands, RealmHandler}; use simulation::{SimulationCommand, SimulationHandler}; @@ -135,6 +137,10 @@ async fn main() -> anyhow::Result<()> { .as_ref() .map(|name| node_config.info.name = name.clone()); + // necessary to grab the variable for lifetime purposes. + let node_name = args.name.clone().unwrap_or("unknown".into()); + let _influx = Metrics::setup(node_name); + log::info!( "Starting app with version {}/{}", SIGNAL_VERSION, @@ -147,9 +153,11 @@ async fn main() -> anyhow::Result<()> { match args.command.clone() { Some(cmd) => match cmd { Commands::Simulation(_) => { - let randtime: u64 = random::() % args.bootwait_max; - log::info!("Waiting {}ms before running this node...", randtime); - wait_ms(randtime).await; + if args.bootwait_max != 0 { + let randtime = random::() % args.bootwait_max; + log::info!("Waiting {}ms before running this node...", randtime); + wait_ms(randtime).await; + } } _ => {} }, @@ -222,6 +230,16 @@ impl Fledger { wait_ms(1000).await; + absolute_counter!("fledger_iterations_total", count as u64); + + if !self.ds.stats.borrow().realm_stats.is_empty() { + let allstats = self.ds.stats.borrow(); + let stats = allstats.realm_stats.iter().next().unwrap().1; + + absolute_counter!("dht_storage_flos_total", stats.flos as u64); + absolute_counter!("dht_storage_size_bytes", stats.size as u64) + } + if match state { FledgerState::Connected(i) => self.dr.stats.borrow().active >= i, FledgerState::DHTAvailable => !self.ds.stats.borrow().realm_stats.is_empty(), diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs index b4c82a1f..fd78dd75 100644 --- a/cli/fledger/src/metrics.rs +++ b/cli/fledger/src/metrics.rs @@ -7,8 +7,8 @@ pub struct Metrics {} impl Metrics { pub fn setup(node_name: String) -> InfluxRecorderHandle { log::info!("Setting up metrics"); - let metrics_file = - File::create("/tmp/out.metrics").expect("could not create /tmp/out.metrics"); + let metrics_file = File::create(format!("/tmp/{}.metrics", node_name)) + .expect(format!("could not create /tmp/{}.metrics", node_name).as_ref()); return InfluxBuilder::new() .with_duration(Duration::from_secs(1)) .with_writer(metrics_file) diff --git a/cli/flsignal/Cargo.toml b/cli/flsignal/Cargo.toml index 7dd1c14d..c6f13e0d 100644 --- a/cli/flsignal/Cargo.toml +++ b/cli/flsignal/Cargo.toml @@ -12,13 +12,16 @@ keywords = ["network", "signalling", "webrtc"] categories = ["network-programming"] [dependencies] -flmodules = {path = "../../flmodules", version = "0.9"} -flarch = {path = "../../flarch", version = "0.9"} +flmodules = { path = "../../flmodules", version = "0.9" } +flarch = { path = "../../flarch", version = "0.9" } -anyhow = {version = "1", features = ["backtrace"]} +anyhow = { version = "1", features = ["backtrace"] } clap = "4" clap-verbosity-flag = "3" env_logger = "0.11" log = "0.4" thiserror = "2" tokio = "1" + +metrics = "0.21.1" +metrics-exporter-influx = { version = "0.2.2", path = "../../metrics-exporter-influx" } diff --git a/cli/flsignal/src/main.rs b/cli/flsignal/src/main.rs index 1be7e90e..bbfaef0a 100644 --- a/cli/flsignal/src/main.rs +++ b/cli/flsignal/src/main.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{fs::File, str::FromStr, time::Duration}; use clap::Parser; use flarch::web_rtc::web_socket_server::WebSocketServer; @@ -6,6 +6,7 @@ use flmodules::{ flo::realm::RealmID, network::signal::{SignalConfig, SignalOut, SignalServer}, }; +use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; /// Fledger signalling server #[derive(Parser, Debug)] @@ -24,6 +25,18 @@ struct Args { max_list_len: Option, } +fn setup_metrics(node_name: String) -> InfluxRecorderHandle { + log::info!("Setting up metrics"); + let metrics_file = File::create(format!("/tmp/{}.metrics", node_name)) + .expect(format!("could not create /tmp/{}.metrics", node_name).as_ref()); + return InfluxBuilder::new() + .with_duration(Duration::from_secs(1)) + .with_writer(metrics_file) + .add_global_tag("node_name", node_name) + .install() + .expect("could not setup influx recorder"); +} + #[tokio::main] async fn main() -> anyhow::Result<()> { let args = Args::parse(); @@ -33,6 +46,8 @@ async fn main() -> anyhow::Result<()> { logger.parse_env("RUST_LOG"); logger.try_init().expect("Failed to initialize logger"); + let _influx = setup_metrics("flsignal".into()); + let wss = WebSocketServer::new(8765).await?; let system_realm = args.system_realm.and_then(|sr| RealmID::from_str(&sr).ok()); log::info!("System realm config is: {:?}", system_realm); diff --git a/flarch/Cargo.toml b/flarch/Cargo.toml index 39e152a5..0983fe8d 100644 --- a/flarch/Cargo.toml +++ b/flarch/Cargo.toml @@ -16,7 +16,7 @@ node = ["flmacro/node"] [dependencies] flmacro = { version = "0.9", path = "../flmacro" } -anyhow = {version = "1", features = ["backtrace"]} +anyhow = { version = "1", features = ["backtrace"] } bytes = "1.9" chrono = "0.4" enum-display = "0.1" @@ -37,6 +37,7 @@ serde = { version = "1", features = ["derive"] } sha2 = "0.10" rmp-serde = "1" btparse = "0.2" +metrics = "0.21.1" # For libc [target.'cfg(target_family="unix")'.dependencies] @@ -52,26 +53,26 @@ wasm-bindgen-test = "0.3" wasmtimer = "0.4" serde-wasm-bindgen = { version = "0.6" } web-sys = { version = "0.3", features = [ - 'Window', - "Storage", - "console", - "MessageEvent", - "RtcConfiguration", - "RtcDataChannel", - "RtcDataChannelEvent", - "RtcDataChannelState", - "RtcIceCandidate", - "RtcIceCandidateInit", - "RtcIceConnectionState", - "RtcIceGatheringState", - "RtcPeerConnection", - "RtcPeerConnectionIceEvent", - "RtcSdpType", - "RtcSessionDescriptionInit", - "RtcSignalingState", - "ErrorEvent", - "MessageEvent", - "WebSocket", + 'Window', + "Storage", + "console", + "MessageEvent", + "RtcConfiguration", + "RtcDataChannel", + "RtcDataChannelEvent", + "RtcDataChannelState", + "RtcIceCandidate", + "RtcIceCandidateInit", + "RtcIceConnectionState", + "RtcIceGatheringState", + "RtcPeerConnection", + "RtcPeerConnectionIceEvent", + "RtcSdpType", + "RtcSessionDescriptionInit", + "RtcSignalingState", + "ErrorEvent", + "MessageEvent", + "WebSocket", ] } # [dev-dependencies] diff --git a/flarch/src/broker.rs b/flarch/src/broker.rs index 872f7abe..b092dbd2 100644 --- a/flarch/src/broker.rs +++ b/flarch/src/broker.rs @@ -42,6 +42,7 @@ use std::{ }; use futures::lock::Mutex; +use metrics::counter; use thiserror::Error; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; @@ -713,6 +714,11 @@ impl Intern { .map(|(_, msg)| msg.clone()) .collect(); + counter!( + "flarch_broker_tap_out_bytes", + msgs.clone().iter().map(|msg| size_of_val(msg) as u64).sum() + ); + let type_str = self.type_str(); for (i, ss) in self.subsystems.iter_mut() { if let Err(e) = ss.send_tap_out(msgs.clone()).await { @@ -738,6 +744,11 @@ impl Intern { .map(|(_, msg)| msg.clone()) .collect(); + counter!( + "flarch_broker_tap_in_bytes", + msgs.iter().map(|msg| size_of_val(msg) as u64).sum() + ); + let type_str = self.type_str(); for (i, ss) in self.subsystems.iter_mut() { if let Err(e) = ss.send_tap_in(msgs.clone()).await { diff --git a/flarch/src/web_rtc/libc/web_socket_client.rs b/flarch/src/web_rtc/libc/web_socket_client.rs index 5251aa52..c61f4035 100644 --- a/flarch/src/web_rtc/libc/web_socket_client.rs +++ b/flarch/src/web_rtc/libc/web_socket_client.rs @@ -9,6 +9,8 @@ use crate::broker::{Broker, SubsystemHandler}; use crate::tasks::wait_ms; use crate::web_rtc::websocket::{BrokerWSClient, WSClientIn, WSClientOut, WSError}; +use metrics::counter; + pub struct WebSocketClient { url: String, write: Option>, tungstenite::Message>>, @@ -76,6 +78,7 @@ impl WebSocketClient { impl SubsystemHandler for WebSocketClient { async fn messages(&mut self, msgs: Vec) -> Vec { for msg in msgs { + counter!("flarch_ws_client_sent_bytes", size_of_val(&msg) as u64); match msg { WSClientIn::Message(msg) => { if let Some(mut write) = self.write.as_mut() { diff --git a/flarch/src/web_rtc/libc/web_socket_server.rs b/flarch/src/web_rtc/libc/web_socket_server.rs index 27e35c99..f852dafc 100644 --- a/flarch/src/web_rtc/libc/web_socket_server.rs +++ b/flarch/src/web_rtc/libc/web_socket_server.rs @@ -19,6 +19,8 @@ use crate::{ web_rtc::websocket::BrokerWSServer, }; +use metrics::counter; + pub struct WebSocketServer { connections: Arc>>, conn_thread: JoinHandle<()>, @@ -69,6 +71,7 @@ impl WebSocketServer { impl SubsystemHandler for WebSocketServer { async fn messages(&mut self, from_broker: Vec) -> Vec { for msg in from_broker { + counter!("flarch_ws_server_recv_bytes", size_of_val(&msg) as u64); match msg { WSServerIn::Message(id, msg) => { let mut connections = self.connections.lock().await; diff --git a/flmodules/Cargo.toml b/flmodules/Cargo.toml index e9aeb0e8..913efa6a 100644 --- a/flmodules/Cargo.toml +++ b/flmodules/Cargo.toml @@ -46,10 +46,11 @@ serde_json = "1" names = { version = "0.14", default-features = false } bytes = { version = "1.7.1", features = ["serde"] } reqwest = { version = "0.12", features = [ - "stream", - "rustls-tls", + "stream", + "rustls-tls", ], default-features = false } num-bigint = { version = "0.4.6", features = ["serde"] } +metrics = "0.21.1" [dev-dependencies] flmodules = { path = ".", features = ["testing"] } diff --git a/flmodules/src/network/broker.rs b/flmodules/src/network/broker.rs index aac054e3..2422951e 100644 --- a/flmodules/src/network/broker.rs +++ b/flmodules/src/network/broker.rs @@ -11,6 +11,7 @@ use core::panic; use itertools::concat; +use metrics::counter; use std::{fmt, time::Duration}; use thiserror::Error; use tokio::sync::{mpsc::UnboundedReceiver, watch}; @@ -171,6 +172,8 @@ impl Messages { /// This can be either messages requested by this node, or connection /// setup requests from another node. async fn msg_ws(&mut self, msg: WSClientOut) -> Vec { + counter!("network_broker_recv_ws_bytes", size_of_val(&msg) as u64); + let msg_node_str = match msg { WSClientOut::Message(msg) => msg, WSClientOut::Error(e) => { @@ -230,6 +233,8 @@ impl Messages { } async fn msg_call(&mut self, msg: NetworkIn) -> anyhow::Result> { + counter!("network_broker_recv_call_bytes", size_of_val(&msg) as u64); + match msg { NetworkIn::MessageToNode(id, msg_str) => { log::trace!( @@ -266,6 +271,11 @@ impl Messages { } async fn msg_node(&mut self, id: U256, msg_nc: NCOutput) -> Vec { + counter!( + "network_broker_recv_node_bytes", + size_of_val(&msg_nc) as u64 + ); + match msg_nc { NCOutput::Connected(_) => vec![NetworkOut::Connected(id)], NCOutput::Disconnected(_) => vec![NetworkOut::Disconnected(id)], @@ -346,6 +356,7 @@ impl SubsystemHandler for Messages { "{}: Processing message {msg}", self.node_config.info.get_id() ); + counter!("network_recv_bytes", size_of_val(&msg) as u64); match msg { NetworkIn::WebSocket(ws) => out.extend(self.msg_ws(ws).await), NetworkIn::WebRTC(WebRTCConnOutput::Message(id, msg)) => { @@ -396,6 +407,7 @@ impl NetworkWebRTC { loop { let msg = self.tap.recv().await; if let Some(msg_reply) = msg { + counter!("network_recv_bytes", size_of_val(&msg_reply) as u64); return msg_reply; } } @@ -405,6 +417,7 @@ impl NetworkWebRTC { /// The message is of type [`NetworkIn`], as this is what the user can /// send to the [`Network`]. pub fn send(&mut self, msg: NetworkIn) -> anyhow::Result<()> { + counter!("network_broker_rtc_sent_bytes", size_of_val(&msg) as u64); self.broker_net.emit_msg_in(msg) } From 08175ac18fb4125843cc0c555722e388937e3dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 28 Apr 2025 07:44:50 +0200 Subject: [PATCH 25/43] feat: simulation create tag an and read tag --- Cargo.lock | 4 +- cli/fledger/src/simulation.rs | 185 ++++++++++++++++++++++++-- flmodules/src/dht_storage/broker.rs | 11 ++ flmodules/src/dht_storage/core.rs | 4 + flmodules/src/dht_storage/messages.rs | 24 ++-- flmodules/src/flo/blob.rs | 4 + 6 files changed, 212 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec7ba8b0..6ff6b5f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1141,9 +1141,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc 0.2.172", "windows-sys 0.59.0", diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index a9bcd836..bb4d7a71 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,7 +1,14 @@ -use crate::{metrics::Metrics, Fledger}; +use std::any::type_name; + +use crate::Fledger; use clap::{arg, Args, Subcommand}; use flarch::{nodeids::U256, tasks::wait_ms}; -use flmodules::gossip_events::core::Event; +use flcrypto::tofrombytes::ToFromBytes; +use flmodules::{ + dht_storage::realm_view::RealmView, + flo::blob::{BlobAccess, BlobTag}, + gossip_events::core::Event, +}; use metrics::{absolute_counter, increment_counter}; #[derive(Args, Debug, Clone)] @@ -28,6 +35,16 @@ pub enum SimulationSubcommand { }, DhtJoinRealm {}, + + CreateTag { + #[arg(long)] + tag: String, + }, + + FetchTag { + #[arg(long)] + tag: String, + }, } pub struct SimulationHandler {} @@ -39,6 +56,8 @@ impl SimulationHandler { Self::run_chat(f, command, send_msg, recv_msg).await } SimulationSubcommand::DhtJoinRealm {} => Self::run_dht_join_realm(f).await, + SimulationSubcommand::CreateTag { tag } => Self::run_dht_create_tag(f, tag).await, + SimulationSubcommand::FetchTag { tag } => Self::run_dht_fetch_tag(f, tag).await, } } @@ -50,8 +69,6 @@ impl SimulationHandler { ) -> anyhow::Result<()> { f.loop_node(crate::FledgerState::Connected(1)).await?; - let node_name = f.args.name.clone().unwrap_or("unknown".into()); - if let Some(ref msg) = recv_msg { log::info!("Waiting for chat message {}.", msg); } @@ -63,9 +80,6 @@ impl SimulationHandler { let mut acked_msg_ids: Vec = Vec::new(); - // necessary to grab the variable for lifetime purposes. - let _influx = Metrics::setup(node_name); - loop { wait_ms(1000).await; @@ -90,7 +104,7 @@ impl SimulationHandler { .iter() .any(|ev| ev.msg.eq(msg)) { - log::info!("RECV_CHAT_MSG TRIGGERED"); + log::info!("SIMULATION END"); f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); } @@ -102,10 +116,165 @@ impl SimulationHandler { f.loop_node(crate::FledgerState::DHTAvailable).await?; log::info!("SIMULATION END"); + absolute_counter!("fledger_realms_total", 1); + + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } + + async fn run_dht_create_tag(mut f: Fledger, tag: String) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::DHTAvailable).await?; + absolute_counter!("fledger_dht_connected", 1); + + log::info!("DHT CONNECTED"); + + //let router = f.node.dht_router.unwrap(); + let ds = f.node.dht_storage.as_mut().unwrap(); + let mut rv = RealmView::new_first(ds.clone()).await?; + + // Send a Flo tag blob + log::info!("Storing tag in DHT {}.", tag); + let flo_tag = rv + .create_tag(&tag, None, flcrypto::access::Condition::Pass, &[]) + .unwrap(); + log::info!( + "tag {}/{}/{} | {}", + flo_tag.flo_id(), + flo_tag.realm_id(), + flo_tag.version(), + flo_tag.values().iter().next().unwrap().1, + ); + + let _ = ds.store_flo(flo_tag.flo().clone()); + let _ = ds.propagate(); + ds.broker.settle(Vec::new()).await?; + + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_end", 1); + + ds.get_flos(&rv.realm.realm_id()) + .await + .unwrap() + .iter() + .for_each(|flo| { + let flo_type = type_name::(); + if flo.flo_type() == flo_type { + let tag = BlobTag::from_rmp_bytes(flo_type, &flo.data()).unwrap(); + log::info!( + "tag found {}/{}/{} | {}", + flo.flo_id(), + flo.realm_id(), + flo.version(), + tag.0.values().iter().next().unwrap().1, + ) + } + }); + + // if let Some(ref tags) = rv.tags { + // log::info!("storage amt: {}", tags.storage.iter().count()); + // + // let tagname = tags + // .storage + // .iter() + // .next() + // .unwrap() + // .1 // first tag stored + // .values() + // .iter() + // .next() + // .unwrap() + // .1; // name of tag + // log::info!("tag found: {}", tagname); + // } else { + // log::info!("NOTICE: tag not found.") + // } + f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); } + async fn run_dht_fetch_tag(mut f: Fledger, tag: String) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::DHTAvailable).await?; + absolute_counter!("fledger_dht_connected", 1); + + log::info!("DHT CONNECTED"); + + let ds = f.node.dht_storage.as_mut().unwrap(); + let mut rv = RealmView::new_first(ds.clone()).await?; + + loop { + wait_ms(1000).await; + + // let fledger_connected_total = f.node.nodes_connected()?.len(); // TODO: does not + // compile. + //absolute_counter!("fledger_connected_total", fledger_connected_total as u64); + increment_counter!("fledger_iterations_total"); + + rv.update_tags().await?; + + let flos = ds.get_flos(&rv.realm.realm_id()).await.unwrap().clone(); + // flos.iter().for_each(|flo| { + // log::info!( + // "flo found {}/{}/{} [{}]", + // flo.flo_id(), + // flo.realm_id(), + // flo.version(), + // flo.flo_type(), + // ) + // }); + + let mut tags = flos + .iter() + .filter(|flo| flo.flo_type() == type_name::()) + .map(|flo| BlobTag::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); + + tags.clone().for_each(|tag| { + log::info!("tag found {}", tag.0.values().iter().next().unwrap().1) + }); + + if tags.any(|flotag| { + flotag + .0 + .values() + .iter() + .next() + .is_some_and(|tagname| *tagname.1 == tag) + }) { + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_end", 1); + f.loop_node(crate::FledgerState::Forever).await?; + + return Ok(()); + } else { + log::info!("Tag not found..."); + } + // tags.storage + // .iter() + // .any(|flotag| flotag.1.values().get(&tag).is_some()); + // //.any(|flotag| flotag.1.values().iter().next().unwrap().1.eq(&tag)); + // //.any(|tag| tag.1.values() tag.1.cache().0.get_blob_mut().values.get(tag).is_some()); + // { + // log::info!("SIMULATION END"); + // absolute_counter!("fledger_simulation_end", 1); + // f.loop_node(crate::FledgerState::Forever).await?; + // + // return Ok(()); + // } + } + } + + // async fn run_dht_request_random_flow(mut f: Fledger) -> anyhow::Result<()> { + // let ds = f.node.dht_storage.unwrap(); + // let rv = RealmView::new_first(ds.clone()).await?; + // + // // To send requests for random floID + // // let realm_id = ds.get_realm_ids().await?.first().unwrap(); + // let realm_id = rv.realm.realm_id(); + // let res = ds.get_flo(&GlobalID::new(realm_id, FloID::rnd())).await; + // + // return Ok(()); + // } + fn log_new_messages(f: &Fledger, acked_msg_ids: &mut Vec) { let chat_events = f.node.gossip.as_ref().unwrap().chat_events(); let chats: Vec<&Event> = chat_events diff --git a/flmodules/src/dht_storage/broker.rs b/flmodules/src/dht_storage/broker.rs index 336e4b05..e3c76784 100644 --- a/flmodules/src/dht_storage/broker.rs +++ b/flmodules/src/dht_storage/broker.rs @@ -36,6 +36,7 @@ pub(super) const MODULE_NAME: &str = "DHTStorage"; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum DHTStorageIn { + ReadFlos(RealmID), StoreFlo(Flo), ReadFlo(GlobalID), ReadCuckooIDs(GlobalID), @@ -47,6 +48,7 @@ pub enum DHTStorageIn { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum DHTStorageOut { + Flos(Vec), FloValue(FloCuckoo), FloValues(Vec), RealmIDs(Vec), @@ -215,6 +217,15 @@ impl DHTStorage { .await?) } + pub async fn get_flos(&mut self, realm_id: &RealmID) -> anyhow::Result> { + Ok(self + .send_wait(DHTStorageIn::ReadFlos(realm_id.clone()), &|msg| match msg { + DHTStorageOut::Flos(flos) => Some(flos.clone()), + _ => None, + }) + .await?) + } + pub fn sync(&mut self) -> anyhow::Result<()> { Ok(self.broker.emit_msg_in(DHTStorageIn::SyncFromNeighbors)?) } diff --git a/flmodules/src/dht_storage/core.rs b/flmodules/src/dht_storage/core.rs index 2e10a02f..5b7ebd0b 100644 --- a/flmodules/src/dht_storage/core.rs +++ b/flmodules/src/dht_storage/core.rs @@ -175,6 +175,10 @@ impl RealmStorage { .collect() } + pub fn get_flos(&self) -> Vec { + return self.flos.iter().map(|flo| flo.1.flo.clone()).collect_vec(); + } + pub fn store_cuckoo_ids(&mut self, parent: &FloID, cuckoos: Vec) { for cuckoo in cuckoos { self.store_cuckoo_id(parent, cuckoo); diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index c08f511b..4d42d561 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -190,7 +190,11 @@ impl Messages { .iter() .flat_map(|realm| realm.1.get_all_flo_cuckoos()) .collect::>(), - ).into()] + ) + .into()] + } + DHTStorageIn::ReadFlos(realm_id) => { + vec![DHTStorageOut::Flos(self.realms.get(&realm_id).unwrap().get_flos()).into()] } } } @@ -372,14 +376,14 @@ impl Messages { // Either its realm is already known, or it is a new realm. // When 'true' is returned, then the flo has been stored. fn upsert_flo(&mut self, flo: Flo) -> bool { - // log::trace!( - // "{} store_flo {}({}/{}) {}", - // self.our_id, - // flo.flo_type(), - // flo.flo_id(), - // flo.realm_id(), - // flo.version() - // ); + log::info!( + "{} store_flo {}({}/{}) {}", + self.our_id, + flo.flo_type(), + flo.flo_id(), + flo.realm_id(), + flo.version() + ); // log::info!( // "{} has realm: {}", // self.our_id, @@ -454,7 +458,7 @@ impl SubsystemHandler for Messages { .map_or(vec![], |msg| vec![msg]) } }) - // .inspect(|msg| log::debug!("{_id}: Out: {msg:?}")) + //.inspect(|msg| log::debug!("{_id}: Out: {msg:?}")) .collect() } } diff --git a/flmodules/src/flo/blob.rs b/flmodules/src/flo/blob.rs index 6d23725f..fd18c18a 100644 --- a/flmodules/src/flo/blob.rs +++ b/flmodules/src/flo/blob.rs @@ -171,6 +171,10 @@ impl FloBlobTag { pub fn blob_id(&self) -> BlobID { (*self.flo_id()).into() } + + pub fn values(&self) -> &HashMap { + (&(*self.get_blob().values())).into() + } } impl BlobAccess for FloBlobTag { From 9424cef01647595da755d10a2b42e41ae0b1a31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Thu, 22 May 2025 08:45:54 +0200 Subject: [PATCH 26/43] feat: deploy binaries to file server --- deploy-binaries.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/deploy-binaries.sh b/deploy-binaries.sh index 0b70e2cf..0fed307b 100755 --- a/deploy-binaries.sh +++ b/deploy-binaries.sh @@ -17,8 +17,18 @@ if ! test -f "target-common/x86_64-unknown-linux-musl/release/fledger"; then exit 1 fi -echo "Copying fledger and flsignal binaries..." +echo "Uploading fledger and flsignal binaries..." scp \ target-common/x86_64-unknown-linux-musl/release/fledger \ target-common/x86_64-unknown-linux-musl/release/flsignal \ - sphere-fledger: + sphere-fledger:/usr/share/caddy/ + +# echo "Pushing fledger and flsignal binaries to github..." +# cp \ +# target-common/x86_64-unknown-linux-musl/release/fledger \ +# target-common/x86_64-unknown-linux-musl/release/flsignal \ +# ../fledger-binaries/ +# cd ../fledger-binaries/ || exit 1 +# git add . || exit 1 +# git commit -m "deploy" || exit 1 +# git push || exit 1 From 638ae751038c32fdbf0d860f1828d803962b6897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Thu, 22 May 2025 08:46:09 +0200 Subject: [PATCH 27/43] feat: exp12 and malicious nodes --- Cargo.lock | 1 + cli/fledger/Cargo.toml | 1 + cli/fledger/src/main.rs | 24 ++- cli/fledger/src/metrics.rs | 4 +- cli/fledger/src/realm.rs | 8 +- cli/fledger/src/simulation.rs | 280 ++++++++++++++++++++++++-- flmodules/src/dht_storage/core.rs | 5 + flmodules/src/dht_storage/messages.rs | 51 +++-- 8 files changed, 327 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ff6b5f4..aaa7c8d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,6 +1290,7 @@ dependencies = [ "log", "metrics", "metrics-exporter-influx", + "rand 0.9.1", "thiserror 2.0.12", "tokio", "webrtc-util", diff --git a/cli/fledger/Cargo.toml b/cli/fledger/Cargo.toml index 61dc5de1..aed80cc3 100644 --- a/cli/fledger/Cargo.toml +++ b/cli/fledger/Cargo.toml @@ -27,3 +27,4 @@ tokio = "1" webrtc-util = "0.10" metrics = "0.21.1" metrics-exporter-influx = { version = "0.2.2", path = "../../metrics-exporter-influx" } +rand = "0.9.1" diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index c768792e..821e34f3 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -91,6 +91,14 @@ pub struct Args { #[arg(long, default_value = "0")] bootwait_max: u64, + /// Run the node but refuse to forward data + #[arg(long, default_value = "false")] + evil_noforward: bool, + + /// Sampling rate for metrics + #[arg(long, default_value = "1000")] + sampling_rate_ms: u32, + #[command(subcommand)] command: Option, } @@ -139,7 +147,7 @@ async fn main() -> anyhow::Result<()> { // necessary to grab the variable for lifetime purposes. let node_name = args.name.clone().unwrap_or("unknown".into()); - let _influx = Metrics::setup(node_name); + let _influx = Metrics::setup(node_name, args.sampling_rate_ms); log::info!( "Starting app with version {}/{}", @@ -165,6 +173,14 @@ async fn main() -> anyhow::Result<()> { } } + unsafe { + flmodules::dht_storage::messages::EVIL_NO_FORWARD = args.evil_noforward; + } + absolute_counter!( + "fledger_evil_noforward", + if args.evil_noforward { 1 } else { 0 } + ); + let cc = if args.disable_turn_stun { ConnectionConfig::from_signal(&args.signal_url) } else { @@ -197,7 +213,7 @@ impl Fledger { ds: node.dht_storage.as_ref().unwrap().clone(), dr: node.dht_router.as_ref().unwrap().clone(), node, - args, + args: args.clone(), }; match f.args.command.clone() { @@ -206,7 +222,9 @@ impl Fledger { Commands::Crypto {} => todo!(), Commands::Stats {} => todo!(), Commands::Page { command } => Page::run(f, command).await, - Commands::Simulation(command) => SimulationHandler::run(f, command).await, + Commands::Simulation(command) => { + SimulationHandler::run(f, command, args.sampling_rate_ms).await + } }, None => f.loop_node(FledgerState::Forever).await, } diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs index fd78dd75..647d3b97 100644 --- a/cli/fledger/src/metrics.rs +++ b/cli/fledger/src/metrics.rs @@ -5,12 +5,12 @@ use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; pub struct Metrics {} impl Metrics { - pub fn setup(node_name: String) -> InfluxRecorderHandle { + pub fn setup(node_name: String, sampling_rate_ms: u32) -> InfluxRecorderHandle { log::info!("Setting up metrics"); let metrics_file = File::create(format!("/tmp/{}.metrics", node_name)) .expect(format!("could not create /tmp/{}.metrics", node_name).as_ref()); return InfluxBuilder::new() - .with_duration(Duration::from_secs(1)) + .with_duration(Duration::from_millis(sampling_rate_ms.into())) .with_writer(metrics_file) .add_global_tag("node_name", node_name) .install() diff --git a/cli/fledger/src/realm.rs b/cli/fledger/src/realm.rs index f013374f..4b8d4545 100644 --- a/cli/fledger/src/realm.rs +++ b/cli/fledger/src/realm.rs @@ -1,5 +1,5 @@ use clap::Subcommand; -use flcrypto::{access::Condition, signer::SignerTrait}; +use flcrypto::access::Condition; use flmodules::{ dht_storage::{core::RealmConfig, realm_view::RealmViewBuilder}, flo::realm::Realm, @@ -70,10 +70,10 @@ impl RealmHandler { "danu".to_string(), INDEX_HTML.to_string(), None, - cond.clone(), - signers.clone(), + Condition::Pass, + vec![], ) - .root_tag("danu".to_string(), None, cond.clone(), signers) + .root_tag("danu".to_string(), None, Condition::Pass, vec![]) .build() .await?; diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index bb4d7a71..0364c864 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,15 +1,21 @@ -use std::any::type_name; +use std::{any::type_name, time::Duration}; use crate::Fledger; +use anyhow::Error; use clap::{arg, Args, Subcommand}; use flarch::{nodeids::U256, tasks::wait_ms}; use flcrypto::tofrombytes::ToFromBytes; use flmodules::{ dht_storage::realm_view::RealmView, - flo::blob::{BlobAccess, BlobTag}, + flo::{ + blob::{BlobAccess, BlobPage, BlobTag}, + flo::FloWrapper, + realm::GlobalID, + }, gossip_events::core::Event, }; use metrics::{absolute_counter, increment_counter}; +use tokio::time::{timeout, Instant}; #[derive(Args, Debug, Clone)] pub struct SimulationCommand { @@ -45,12 +51,31 @@ pub enum SimulationSubcommand { #[arg(long)] tag: String, }, + + CreatePageWithFillers { + #[arg(long)] + filler_amount: u32, + + #[arg(long)] + page_size: u32, + + #[arg(long)] + settling_delay: u32, + }, + FetchPage { + #[arg(long, default_value = "20000")] + timeout_ms: u32, + }, } pub struct SimulationHandler {} impl SimulationHandler { - pub async fn run(f: Fledger, command: SimulationCommand) -> anyhow::Result<()> { + pub async fn run( + f: Fledger, + command: SimulationCommand, + sampling_rate_ms: u32, + ) -> anyhow::Result<()> { match command.subcommand.clone() { SimulationSubcommand::Chat { send_msg, recv_msg } => { Self::run_chat(f, command, send_msg, recv_msg).await @@ -58,6 +83,17 @@ impl SimulationHandler { SimulationSubcommand::DhtJoinRealm {} => Self::run_dht_join_realm(f).await, SimulationSubcommand::CreateTag { tag } => Self::run_dht_create_tag(f, tag).await, SimulationSubcommand::FetchTag { tag } => Self::run_dht_fetch_tag(f, tag).await, + SimulationSubcommand::CreatePageWithFillers { + filler_amount, + page_size, + settling_delay, + } => { + Self::run_dht_create_page_with_fillers(f, filler_amount, page_size, settling_delay) + .await + } + SimulationSubcommand::FetchPage { timeout_ms } => { + Self::run_dht_fetch_simulation_page(f, sampling_rate_ms, timeout_ms).await + } } } @@ -170,24 +206,22 @@ impl SimulationHandler { } }); - // if let Some(ref tags) = rv.tags { - // log::info!("storage amt: {}", tags.storage.iter().count()); - // - // let tagname = tags - // .storage - // .iter() - // .next() - // .unwrap() - // .1 // first tag stored - // .values() - // .iter() - // .next() - // .unwrap() - // .1; // name of tag - // log::info!("tag found: {}", tagname); - // } else { - // log::info!("NOTICE: tag not found.") - // } + let tags = rv.tags; + + log::info!("storage amt: {}", tags.storage.iter().count()); + + let tagname = tags + .storage + .iter() + .next() + .unwrap() + .1 // first tag stored + .values() + .iter() + .next() + .unwrap() + .1; // name of tag + log::info!("tag found: {}", tagname); f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); @@ -275,6 +309,210 @@ impl SimulationHandler { // return Ok(()); // } + async fn run_dht_create_page_with_fillers( + mut f: Fledger, + filler_amount: u32, + page_size: u32, + settling_delay: u32, + ) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::DHTAvailable).await?; + absolute_counter!("fledger_dht_connected", 1); + + log::info!("DHT CONNECTED"); + + //let router = f.node.dht_router.unwrap(); + let ds = f.node.dht_storage.as_mut().unwrap(); + let mut rv = RealmView::new_first(ds.clone()).await?; + + log::info!("[Create filler pages]"); + for i in 0..filler_amount { + let flo_page = rv + .create_http( + &format!("simulation-filler-{}", i.to_string()), + String::from_utf8(vec![b'-'; page_size as usize])?, + None, + flcrypto::access::Condition::Pass, + &[], + ) + .await + .unwrap(); + + let page_content = + String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()) + .unwrap(); + + log::info!( + "page {}/{}/{} | {} | {} ({}B -> {}B)", + flo_page.flo_id(), + flo_page.realm_id(), + flo_page.version(), + flo_page.values().iter().next().unwrap().1, + page_content.chars().take(50).collect::(), + page_content.size(), + flo_page.size(), + ); + } + + log::info!("[Waiting for fillers to settle]"); + log::info!("{} ms", settling_delay); + + wait_ms(settling_delay as u64).await; + + log::info!("[Sending simulation flo page]"); + let flo_page = rv + .create_http( + "simulation-page", + String::from_utf8(vec![b'o'; page_size as usize])?, + None, + flcrypto::access::Condition::Pass, + &[], + ) + .await + .unwrap(); + + let page_content = + String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()).unwrap(); + + log::info!( + "page {}/{}/{} | {} | {} ({}B -> {}B)", + flo_page.flo_id(), + flo_page.realm_id(), + flo_page.version(), + flo_page.values().iter().next().unwrap().1, + page_content.chars().take(50).collect::(), + page_content.size(), + flo_page.size(), + ); + + let signer = f.node.crypto_storage.get_signer(); + rv.set_realm_service("simulation-page", flo_page.blob_id(), &[&signer]) + .await?; + ds.store_flo(flo_page.flo().clone())?; + ds.propagate()?; + ds.broker.settle(Vec::new()).await?; + + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_success", 1); + + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } + + async fn run_dht_fetch_simulation_page( + mut f: Fledger, + sampling_rate_ms: u32, + timeout_ms: u32, + ) -> anyhow::Result<()> { + let start_instant = Instant::now(); + + let timeout_result = timeout( + Duration::from_millis(timeout_ms.into()), + f.loop_node(crate::FledgerState::DHTAvailable), + ) + .await; + + if timeout_result.is_err() { + log::warn!("SIMULATION TIMEOUT WHILE CONNECTING TO DHT"); + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_timeout", 1); + } + + absolute_counter!("fledger_dht_connected", 1); + + log::info!("DHT CONNECTED"); + + let ds = f.node.dht_storage.as_mut().unwrap(); + + loop { + ds.sync()?; + + if start_instant.elapsed().as_millis() > timeout_ms as u128 { + log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_timeout", 1); + f.loop_node(crate::FledgerState::Forever).await?; + + return Ok(()); + } + + wait_ms(sampling_rate_ms.into()).await; + + increment_counter!("fledger_iterations_total"); + + let rv = RealmView::new_first(ds.clone()).await?; + + let pages = ds.get_flos(&rv.realm.realm_id()).await.unwrap().clone(); + let pages = pages + .iter() + .filter(|flo| flo.flo_type() == type_name::()) + .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); + + let mut had_simulation_page_before_fetch = false; + pages.clone().for_each(|page| { + let page_name = page.0.values().iter().next().unwrap().1; + log::info!("page found {}", page_name); + if page_name == "simulation-page" { + had_simulation_page_before_fetch = true; + } + }); + absolute_counter!("fledger_pages_total", pages.count() as u64); + + let ds_size = ds.stats.borrow().realm_stats.iter().next().unwrap().1.size; + absolute_counter!("fledger_realm_storage_bytes", ds_size as u64); + + let page_id_opt = rv.realm.cache().get_services().get("simulation-page"); + if let Some(page_id) = page_id_opt { + let page_global_id = GlobalID::new(rv.realm.realm_id(), page_id.clone()); + let page_flo_wrapper_result: Result, Error> = + ds.get_flo(&page_global_id).await; + + if let Ok(page_flo_wrapper) = page_flo_wrapper_result { + let page_flo = page_flo_wrapper.flo(); + let page_blob = + BlobPage::from_rmp_bytes(page_flo.flo_type().as_str(), page_flo.data()) + .unwrap(); + let page_content = String::from_utf8( + page_blob + .0 + .datas() + .iter() + .next() + .unwrap() + .1 + .clone() + .to_vec(), + ) + .unwrap_or_default(); + log::info!( + "simulation page found with content: {}", + page_content.chars().take(50).collect::() + ); + + if had_simulation_page_before_fetch { + log::warn!("had simulation page before fetch"); + absolute_counter!("fledger_simulation_stored_before_fetch", 1); + } else { + absolute_counter!("fledger_simulation_stored_before_fetch", 0); + } + + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_success", 1); + f.loop_node(crate::FledgerState::Forever).await?; + + return Ok(()); + } else { + increment_counter!("fledger_simulation_page_fetch_fail_total"); + + log::info!("could not fetch page with id [{}].", page_id); + } + } else { + increment_counter!("fledger_simulation_service_fetch_fail_total"); + + log::info!("page_id not found in services..."); + } + } + } + fn log_new_messages(f: &Fledger, acked_msg_ids: &mut Vec) { let chat_events = f.node.gossip.as_ref().unwrap().chat_events(); let chats: Vec<&Event> = chat_events diff --git a/flmodules/src/dht_storage/core.rs b/flmodules/src/dht_storage/core.rs index 5b7ebd0b..d3cafa1d 100644 --- a/flmodules/src/dht_storage/core.rs +++ b/flmodules/src/dht_storage/core.rs @@ -260,6 +260,11 @@ impl RealmStorage { self.realm_config.max_space ); } + log::warn!( + "realm storage : current({}) / max({})", + self.size, + self.realm_config.max_space + ); is_new_flo } diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 4d42d561..972eac80 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -7,6 +7,7 @@ use flarch::{ platform_async_trait, }; use flcrypto::tofrombytes::ToFromBytes; +use metrics::increment_counter; use serde::{Deserialize, Serialize}; use tokio::sync::watch; @@ -103,6 +104,8 @@ pub struct Messages { tx: Option>, } +pub static mut EVIL_NO_FORWARD: bool = false; + impl Messages { /// Returns a new chat module. pub fn new( @@ -308,13 +311,6 @@ impl Messages { ) .collect() } - MessageNeighbour::RequestFloMetas(realm_id) => self - .realms - .get(&realm_id) - .map(|realm| realm.get_flo_metas()) - .map_or(vec![], |fm| { - vec![MessageNeighbour::AvailableFlos(realm_id, fm)] - }), MessageNeighbour::AvailableFlos(realm_id, flo_metas) => self .realms .get(&realm_id) @@ -322,16 +318,37 @@ impl Messages { .map_or(vec![], |needed| { vec![MessageNeighbour::RequestFlos(realm_id, needed)] }), - MessageNeighbour::RequestFlos(realm_id, flo_ids) => self - .realms - .get(&realm_id) - .map(|realm| { - flo_ids - .iter() - .filter_map(|id| realm.get_flo_cuckoo(id)) - .collect::>() - }) - .map_or(vec![], |flos| vec![MessageNeighbour::Flos(flos)]), + MessageNeighbour::RequestFloMetas(realm_id) => { + if unsafe { !EVIL_NO_FORWARD } { + increment_counter!("fledger_forwarded_flo_meta_requests_total"); + self.realms + .get(&realm_id) + .map(|realm| realm.get_flo_metas()) + .map_or(vec![], |fm| { + vec![MessageNeighbour::AvailableFlos(realm_id, fm)] + }) + } else { + increment_counter!("fledger_blocked_flo_meta_requests_total"); + vec![] + } + } + MessageNeighbour::RequestFlos(realm_id, flo_ids) => { + if unsafe { !EVIL_NO_FORWARD } { + increment_counter!("fledger_forwarded_flo_requests_total"); + self.realms + .get(&realm_id) + .map(|realm| { + flo_ids + .iter() + .filter_map(|id| realm.get_flo_cuckoo(id)) + .collect::>() + }) + .map_or(vec![], |flos| vec![MessageNeighbour::Flos(flos)]) + } else { + increment_counter!("fledger_blocked_flo_requests_total"); + vec![] + } + } MessageNeighbour::Flos(flo_cuckoos) => { for (flo, cuckoos) in flo_cuckoos { self.store_flo(flo.clone()); From eb1ace57ca4fa5a0d1e20b15fef7e87d07ae4a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Fri, 23 May 2025 10:11:13 +0200 Subject: [PATCH 28/43] feat: changes --- cli/fledger/src/realm.rs | 2 +- cli/fledger/src/simulation.rs | 182 +++++++++++++++++++------- flbrowser/Cargo.lock | 36 +++++ flmodules/src/dht_storage/broker.rs | 10 -- flmodules/src/dht_storage/messages.rs | 4 +- 5 files changed, 176 insertions(+), 58 deletions(-) diff --git a/cli/fledger/src/realm.rs b/cli/fledger/src/realm.rs index 4b8d4545..a9460dc4 100644 --- a/cli/fledger/src/realm.rs +++ b/cli/fledger/src/realm.rs @@ -1,5 +1,5 @@ use clap::Subcommand; -use flcrypto::access::Condition; +use flcrypto::{access::Condition, signer::SignerTrait}; use flmodules::{ dht_storage::{core::RealmConfig, realm_view::RealmViewBuilder}, flo::realm::Realm, diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 0364c864..dfc7b591 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -66,6 +66,13 @@ pub enum SimulationSubcommand { #[arg(long, default_value = "20000")] timeout_ms: u32, }, + WaitPages { + #[arg(long, default_value = "20000")] + timeout_ms: u32, + + #[arg(long, default_value = "10")] + amount: u32, + }, } pub struct SimulationHandler {} @@ -94,6 +101,9 @@ impl SimulationHandler { SimulationSubcommand::FetchPage { timeout_ms } => { Self::run_dht_fetch_simulation_page(f, sampling_rate_ms, timeout_ms).await } + SimulationSubcommand::WaitPages { timeout_ms, amount } => { + Self::run_dht_wait_for_pages(f, sampling_rate_ms, timeout_ms, amount).await + } } } @@ -188,23 +198,19 @@ impl SimulationHandler { log::info!("SIMULATION END"); absolute_counter!("fledger_simulation_end", 1); - ds.get_flos(&rv.realm.realm_id()) - .await - .unwrap() - .iter() - .for_each(|flo| { - let flo_type = type_name::(); - if flo.flo_type() == flo_type { - let tag = BlobTag::from_rmp_bytes(flo_type, &flo.data()).unwrap(); - log::info!( - "tag found {}/{}/{} | {}", - flo.flo_id(), - flo.realm_id(), - flo.version(), - tag.0.values().iter().next().unwrap().1, - ) - } - }); + ds.get_flos().await.unwrap().iter().for_each(|flo| { + let flo_type = type_name::(); + if flo.flo_type() == flo_type { + let tag = BlobTag::from_rmp_bytes(flo_type, &flo.data()).unwrap(); + log::info!( + "tag found {}/{}/{} | {}", + flo.flo_id(), + flo.realm_id(), + flo.version(), + tag.0.values().iter().next().unwrap().1, + ) + } + }); let tags = rv.tags; @@ -246,7 +252,7 @@ impl SimulationHandler { rv.update_tags().await?; - let flos = ds.get_flos(&rv.realm.realm_id()).await.unwrap().clone(); + let flos = ds.get_flos().await.unwrap().clone(); // flos.iter().for_each(|flo| { // log::info!( // "flo found {}/{}/{} [{}]", @@ -297,18 +303,6 @@ impl SimulationHandler { } } - // async fn run_dht_request_random_flow(mut f: Fledger) -> anyhow::Result<()> { - // let ds = f.node.dht_storage.unwrap(); - // let rv = RealmView::new_first(ds.clone()).await?; - // - // // To send requests for random floID - // // let realm_id = ds.get_realm_ids().await?.first().unwrap(); - // let realm_id = rv.realm.realm_id(); - // let res = ds.get_flo(&GlobalID::new(realm_id, FloID::rnd())).await; - // - // return Ok(()); - // } - async fn run_dht_create_page_with_fillers( mut f: Fledger, filler_amount: u32, @@ -356,7 +350,15 @@ impl SimulationHandler { log::info!("[Waiting for fillers to settle]"); log::info!("{} ms", settling_delay); - wait_ms(settling_delay as u64).await; + let settling_seconds = settling_delay / 1000; + for _ in 0..settling_seconds { + ds.propagate()?; + ds.sync()?; + ds.broker.settle(Vec::new()).await?; + wait_ms(1000).await; + } + + wait_ms((settling_delay % 1000) as u64).await; log::info!("[Sending simulation flo page]"); let flo_page = rv @@ -387,8 +389,19 @@ impl SimulationHandler { let signer = f.node.crypto_storage.get_signer(); rv.set_realm_service("simulation-page", flo_page.blob_id(), &[&signer]) .await?; + + ds.store_flo(flo_page.flo().clone())?; + ds.propagate()?; + ds.store_flo(flo_page.flo().clone())?; + ds.propagate()?; ds.store_flo(flo_page.flo().clone())?; ds.propagate()?; + ds.store_flo(flo_page.flo().clone())?; + ds.propagate()?; + + ds.sync()?; + ds.sync()?; + ds.broker.settle(Vec::new()).await?; log::info!("SIMULATION END"); @@ -398,12 +411,16 @@ impl SimulationHandler { return Ok(()); } - async fn run_dht_fetch_simulation_page( + async fn run_dht_wait_for_pages( mut f: Fledger, sampling_rate_ms: u32, timeout_ms: u32, + amount: u32, ) -> anyhow::Result<()> { let start_instant = Instant::now(); + absolute_counter!("fledger_simulation_success", 0); + absolute_counter!("fledger_dht_connected", 0); + absolute_counter!("fledger_connected_total", 0); let timeout_result = timeout( Duration::from_millis(timeout_ms.into()), @@ -422,10 +439,87 @@ impl SimulationHandler { log::info!("DHT CONNECTED"); let ds = f.node.dht_storage.as_mut().unwrap(); + let mut rv = RealmView::new_first(ds.clone()).await?; loop { - ds.sync()?; + if start_instant.elapsed().as_millis() > timeout_ms as u128 { + log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_timeout", 1); + f.loop_node(crate::FledgerState::Forever).await?; + + return Ok(()); + } + + wait_ms(sampling_rate_ms.into()).await; + + increment_counter!("fledger_iterations_total"); + + absolute_counter!( + "fledger_connected_total", + f.node.nodes_connected()?.len() as u64 + ); + + rv.update_all().await?; + f.node.dht_storage.as_mut().unwrap().sync()?; + f.node.dht_storage.as_mut().unwrap().propagate()?; + let pages = f + .node + .dht_storage + .as_mut() + .unwrap() + .get_flos() + .await + .unwrap() + .clone(); + let pages = pages + .iter() + .filter(|flo| flo.flo_type() == type_name::()) + .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); + + let page_count = pages.count(); + absolute_counter!("fledger_pages_total", page_count as u64); + + if page_count >= amount as usize { + log::info!("enough pages received"); + log::info!("SIMULATION END"); + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } else { + log::info!("NOT enough pages received..."); + } + } + } + + async fn run_dht_fetch_simulation_page( + mut f: Fledger, + sampling_rate_ms: u32, + timeout_ms: u32, + ) -> anyhow::Result<()> { + let start_instant = Instant::now(); + absolute_counter!("fledger_simulation_success", 0); + + let timeout_result = timeout( + Duration::from_millis(timeout_ms.into()), + f.loop_node(crate::FledgerState::DHTAvailable), + ) + .await; + + if timeout_result.is_err() { + log::warn!("SIMULATION TIMEOUT WHILE CONNECTING TO DHT"); + log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_timeout", 1); + } + + absolute_counter!("fledger_dht_connected", 1); + + log::info!("DHT CONNECTED"); + + let ds = f.node.dht_storage.as_mut().unwrap(); + let mut rv = RealmView::new_first(ds.clone()).await?; + + loop { if start_instant.elapsed().as_millis() > timeout_ms as u128 { log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); log::info!("SIMULATION END"); @@ -439,22 +533,29 @@ impl SimulationHandler { increment_counter!("fledger_iterations_total"); - let rv = RealmView::new_first(ds.clone()).await?; + rv.update_all().await?; - let pages = ds.get_flos(&rv.realm.realm_id()).await.unwrap().clone(); + let pages = ds.get_flos().await.unwrap().clone(); let pages = pages .iter() .filter(|flo| flo.flo_type() == type_name::()) .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); - let mut had_simulation_page_before_fetch = false; + let mut simulation_page_stored_this_iteration = false; pages.clone().for_each(|page| { let page_name = page.0.values().iter().next().unwrap().1; log::info!("page found {}", page_name); if page_name == "simulation-page" { - had_simulation_page_before_fetch = true; + simulation_page_stored_this_iteration = true; } }); + + if simulation_page_stored_this_iteration { + absolute_counter!("fledger_simulation_page_stored", 1); + } else { + absolute_counter!("fledger_simulation_page_stored", 0); + } + absolute_counter!("fledger_pages_total", pages.count() as u64); let ds_size = ds.stats.borrow().realm_stats.iter().next().unwrap().1.size; @@ -488,13 +589,6 @@ impl SimulationHandler { page_content.chars().take(50).collect::() ); - if had_simulation_page_before_fetch { - log::warn!("had simulation page before fetch"); - absolute_counter!("fledger_simulation_stored_before_fetch", 1); - } else { - absolute_counter!("fledger_simulation_stored_before_fetch", 0); - } - log::info!("SIMULATION END"); absolute_counter!("fledger_simulation_success", 1); f.loop_node(crate::FledgerState::Forever).await?; diff --git a/flbrowser/Cargo.lock b/flbrowser/Cargo.lock index 8f95c4a1..f7ee359c 100644 --- a/flbrowser/Cargo.lock +++ b/flbrowser/Cargo.lock @@ -52,6 +52,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -794,6 +806,7 @@ dependencies = [ "futures", "js-sys", "log", + "metrics", "rand 0.8.5", "regex", "rmp-serde", @@ -890,6 +903,7 @@ dependencies = [ "getrandom 0.2.16", "itertools", "log", + "metrics", "names", "num-bigint", "rand 0.8.5", @@ -1556,6 +1570,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metrics" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" +dependencies = [ + "ahash", + "metrics-macros", + "portable-atomic", +] + +[[package]] +name = "metrics-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "mime" version = "0.3.17" diff --git a/flmodules/src/dht_storage/broker.rs b/flmodules/src/dht_storage/broker.rs index e3c76784..a3b17814 100644 --- a/flmodules/src/dht_storage/broker.rs +++ b/flmodules/src/dht_storage/broker.rs @@ -36,7 +36,6 @@ pub(super) const MODULE_NAME: &str = "DHTStorage"; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum DHTStorageIn { - ReadFlos(RealmID), StoreFlo(Flo), ReadFlo(GlobalID), ReadCuckooIDs(GlobalID), @@ -217,15 +216,6 @@ impl DHTStorage { .await?) } - pub async fn get_flos(&mut self, realm_id: &RealmID) -> anyhow::Result> { - Ok(self - .send_wait(DHTStorageIn::ReadFlos(realm_id.clone()), &|msg| match msg { - DHTStorageOut::Flos(flos) => Some(flos.clone()), - _ => None, - }) - .await?) - } - pub fn sync(&mut self) -> anyhow::Result<()> { Ok(self.broker.emit_msg_in(DHTStorageIn::SyncFromNeighbors)?) } diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 972eac80..9a07ac35 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -196,9 +196,6 @@ impl Messages { ) .into()] } - DHTStorageIn::ReadFlos(realm_id) => { - vec![DHTStorageOut::Flos(self.realms.get(&realm_id).unwrap().get_flos()).into()] - } } } @@ -373,6 +370,7 @@ impl Messages { } fn store_flo(&mut self, flo: Flo) -> Vec { + increment_counter!("fledger_ds_store_flo_total"); let mut res = vec![]; if self.upsert_flo(flo.clone()) { // log::info!("{}: store_flo", self.our_id); From ce2214bed73a055d74d96f4b98b550c900373996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 26 May 2025 17:45:54 +0200 Subject: [PATCH 29/43] meeting: 26 may debugging session --- Cargo.lock | 237 ++++++++------------------ cli/fledger/src/simulation.rs | 13 +- flmodules/src/dht_storage/messages.rs | 44 ++++- 3 files changed, 112 insertions(+), 182 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aaa7c8d4..0306794f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" dependencies = [ "async-lock", "cfg-if", @@ -299,7 +299,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.44", + "rustix", "slab", "tracing", "windows-sys 0.59.0", @@ -327,9 +327,9 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" dependencies = [ "async-channel 2.3.1", "async-io", @@ -340,7 +340,7 @@ dependencies = [ "cfg-if", "event-listener 5.4.0", "futures-lite", - "rustix 0.38.44", + "rustix", "tracing", ] @@ -357,9 +357,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" dependencies = [ "async-io", "async-lock", @@ -367,7 +367,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.44", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -442,7 +442,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1611,15 +1611,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "hex" @@ -1803,17 +1797,21 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", + "ipnet", "libc 0.2.172", + "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -2016,6 +2014,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2176,12 +2184,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -2462,11 +2464,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc 0.2.172", ] @@ -2571,7 +2573,7 @@ dependencies = [ "libc 0.2.172", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2698,15 +2700,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.4" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", - "rustix 0.38.44", + "rustix", "tracing", "windows-sys 0.59.0", ] @@ -3054,9 +3056,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" dependencies = [ "async-compression", "base64 0.22.1", @@ -3078,7 +3080,6 @@ dependencies = [ "pin-project-lite", "quinn", "rustls", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -3088,14 +3089,14 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.11", - "windows-registry", + "webpki-roots 1.0.0", ] [[package]] @@ -3200,19 +3201,6 @@ dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.0", - "errno", - "libc 0.2.172", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.0.7" @@ -3222,7 +3210,7 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc 0.2.172", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.59.0", ] @@ -3252,15 +3240,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -3736,7 +3715,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix", "windows-sys 0.59.0", ] @@ -3971,6 +3950,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.0", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -4655,7 +4652,7 @@ dependencies = [ "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -4686,40 +4683,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", -] - [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -4730,7 +4707,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -4739,7 +4716,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -4748,30 +4725,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -4780,96 +4741,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index dfc7b591..364f8430 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -390,16 +390,8 @@ impl SimulationHandler { rv.set_realm_service("simulation-page", flo_page.blob_id(), &[&signer]) .await?; - ds.store_flo(flo_page.flo().clone())?; - ds.propagate()?; - ds.store_flo(flo_page.flo().clone())?; - ds.propagate()?; - ds.store_flo(flo_page.flo().clone())?; - ds.propagate()?; - ds.store_flo(flo_page.flo().clone())?; - ds.propagate()?; + //ds.store_flo(flo_page.flo().clone())?; - ds.sync()?; ds.sync()?; ds.broker.settle(Vec::new()).await?; @@ -457,12 +449,11 @@ impl SimulationHandler { absolute_counter!( "fledger_connected_total", - f.node.nodes_connected()?.len() as u64 + f.node.dht_router.as_ref().unwrap().stats.borrow().active as u64 ); rv.update_all().await?; f.node.dht_storage.as_mut().unwrap().sync()?; - f.node.dht_storage.as_mut().unwrap().propagate()?; let pages = f .node diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 9a07ac35..0c2bf22f 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -7,7 +7,7 @@ use flarch::{ platform_async_trait, }; use flcrypto::tofrombytes::ToFromBytes; -use metrics::increment_counter; +use metrics::{absolute_counter, increment_counter}; use serde::{Deserialize, Serialize}; use tokio::sync::watch; @@ -308,13 +308,24 @@ impl Messages { ) .collect() } - MessageNeighbour::AvailableFlos(realm_id, flo_metas) => self - .realms - .get(&realm_id) - .and_then(|realm| realm.sync_available(&flo_metas)) - .map_or(vec![], |needed| { - vec![MessageNeighbour::RequestFlos(realm_id, needed)] - }), + MessageNeighbour::AvailableFlos(realm_id, flo_metas) => { + log::info!("AvailableFlos: {}", flo_metas.len()); + absolute_counter!( + "fledger_flos_metas_received_from_neighbour", + flo_metas.len() as u64 + ); + self.realms + .get(&realm_id) + .and_then(|realm| realm.sync_available(&flo_metas)) + .map_or(vec![], |needed| { + log::info!("Needed flos: {}", needed.len()); + absolute_counter!( + "fledger_flos_requested_from_neighbour", + needed.len() as u64 + ); + vec![MessageNeighbour::RequestFlos(realm_id, needed)] + }) + } MessageNeighbour::RequestFloMetas(realm_id) => { if unsafe { !EVIL_NO_FORWARD } { increment_counter!("fledger_forwarded_flo_meta_requests_total"); @@ -330,6 +341,11 @@ impl Messages { } } MessageNeighbour::RequestFlos(realm_id, flo_ids) => { + log::info!("RequestFlos: {flo_ids:?}"); + absolute_counter!( + "fledger_flos_ids_received_from_neighbour", // requested + flo_ids.len() as u64 + ); if unsafe { !EVIL_NO_FORWARD } { increment_counter!("fledger_forwarded_flo_requests_total"); self.realms @@ -337,16 +353,26 @@ impl Messages { .map(|realm| { flo_ids .iter() + .take(1) // TODO: remove .filter_map(|id| realm.get_flo_cuckoo(id)) .collect::>() }) - .map_or(vec![], |flos| vec![MessageNeighbour::Flos(flos)]) + .map_or(vec![], |flos| { + log::info!("Flos to send: {}", flos.len()); + absolute_counter!("fledger_flos_sent_to_neighbour", flos.len() as u64); + vec![MessageNeighbour::Flos(flos)] + }) } else { increment_counter!("fledger_blocked_flo_requests_total"); vec![] } } MessageNeighbour::Flos(flo_cuckoos) => { + log::info!("Flos: {}", flo_cuckoos.len()); + absolute_counter!( + "fledger_flos_received_from_neighbour", // received + flo_cuckoos.len() as u64 + ); for (flo, cuckoos) in flo_cuckoos { self.store_flo(flo.clone()); self.realms.get_mut(&flo.realm_id()).map(|realm| { From 5e2dbf094dd7bbdb7f8a48c3a4607413fc2fd24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Wed, 28 May 2025 10:05:04 +0200 Subject: [PATCH 30/43] feat: changes --- cli/fledger/src/main.rs | 1 + cli/fledger/src/simulation.rs | 205 +++++++++++++++++++------- deploy-binaries.sh | 8 +- flarch/src/nodeids.rs | 2 +- flmodules/src/dht_router/messages.rs | 2 + flmodules/src/dht_storage/core.rs | 10 +- flmodules/src/dht_storage/messages.rs | 83 ++++++----- flmodules/src/flo/blob.rs | 14 ++ 8 files changed, 230 insertions(+), 95 deletions(-) diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 821e34f3..2c191d84 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -175,6 +175,7 @@ async fn main() -> anyhow::Result<()> { unsafe { flmodules::dht_storage::messages::EVIL_NO_FORWARD = args.evil_noforward; + flmodules::dht_router::messages::EVIL_NO_FORWARD = args.evil_noforward; } absolute_counter!( "fledger_evil_noforward", diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 364f8430..f25415b1 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,4 +1,4 @@ -use std::{any::type_name, time::Duration}; +use std::{any::type_name, collections::HashMap, str::FromStr, time::Duration, vec}; use crate::Fledger; use anyhow::Error; @@ -6,10 +6,10 @@ use clap::{arg, Args, Subcommand}; use flarch::{nodeids::U256, tasks::wait_ms}; use flcrypto::tofrombytes::ToFromBytes; use flmodules::{ - dht_storage::realm_view::RealmView, + dht_storage::{core::FloConfig, realm_view::RealmView}, flo::{ - blob::{BlobAccess, BlobPage, BlobTag}, - flo::FloWrapper, + blob::{Blob, BlobAccess, BlobID, BlobPage, BlobTag}, + flo::{FloID, FloWrapper}, realm::GlobalID, }, gossip_events::core::Event, @@ -60,11 +60,17 @@ pub enum SimulationSubcommand { page_size: u32, #[arg(long)] - settling_delay: u32, + pages_propagation_delay: u32, + + #[arg(long)] + connection_delay: u32, }, FetchPage { #[arg(long, default_value = "20000")] timeout_ms: u32, + + #[arg(long)] + experiment_id: u32, }, WaitPages { #[arg(long, default_value = "20000")] @@ -93,13 +99,24 @@ impl SimulationHandler { SimulationSubcommand::CreatePageWithFillers { filler_amount, page_size, - settling_delay, + pages_propagation_delay, + connection_delay, } => { - Self::run_dht_create_page_with_fillers(f, filler_amount, page_size, settling_delay) - .await + Self::run_dht_create_page_with_fillers( + f, + filler_amount, + page_size, + pages_propagation_delay, + connection_delay, + ) + .await } - SimulationSubcommand::FetchPage { timeout_ms } => { - Self::run_dht_fetch_simulation_page(f, sampling_rate_ms, timeout_ms).await + SimulationSubcommand::FetchPage { + timeout_ms, + experiment_id, + } => { + Self::run_dht_fetch_simulation_page(f, sampling_rate_ms, timeout_ms, experiment_id) + .await } SimulationSubcommand::WaitPages { timeout_ms, amount } => { Self::run_dht_wait_for_pages(f, sampling_rate_ms, timeout_ms, amount).await @@ -307,23 +324,29 @@ impl SimulationHandler { mut f: Fledger, filler_amount: u32, page_size: u32, - settling_delay: u32, + pages_propagation_delay: u32, + connection_delay: u32, ) -> anyhow::Result<()> { f.loop_node(crate::FledgerState::DHTAvailable).await?; absolute_counter!("fledger_dht_connected", 1); log::info!("DHT CONNECTED"); + log::info!("[Waiting for connections to settle]"); + log::info!("{} ms", connection_delay); + wait_ms(connection_delay as u64).await; + //let router = f.node.dht_router.unwrap(); let ds = f.node.dht_storage.as_mut().unwrap(); let mut rv = RealmView::new_first(ds.clone()).await?; log::info!("[Create filler pages]"); for i in 0..filler_amount { + let page_content = String::from_utf8(vec![b'-'; page_size as usize])?; let flo_page = rv .create_http( &format!("simulation-filler-{}", i.to_string()), - String::from_utf8(vec![b'-'; page_size as usize])?, + page_content.clone(), None, flcrypto::access::Condition::Pass, &[], @@ -331,46 +354,65 @@ impl SimulationHandler { .await .unwrap(); - let page_content = - String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()) - .unwrap(); - log::info!( "page {}/{}/{} | {} | {} ({}B -> {}B)", flo_page.flo_id(), flo_page.realm_id(), flo_page.version(), flo_page.values().iter().next().unwrap().1, - page_content.chars().take(50).collect::(), + page_content.clone().chars().take(50).collect::(), page_content.size(), flo_page.size(), ); } log::info!("[Waiting for fillers to settle]"); - log::info!("{} ms", settling_delay); - - let settling_seconds = settling_delay / 1000; - for _ in 0..settling_seconds { - ds.propagate()?; - ds.sync()?; - ds.broker.settle(Vec::new()).await?; - wait_ms(1000).await; - } + log::info!("{} ms", pages_propagation_delay); - wait_ms((settling_delay % 1000) as u64).await; + ds.broker.settle(Vec::new()).await?; + ds.sync()?; + wait_ms(pages_propagation_delay as u64).await; log::info!("[Sending simulation flo page]"); - let flo_page = rv - .create_http( - "simulation-page", - String::from_utf8(vec![b'o'; page_size as usize])?, - None, - flcrypto::access::Condition::Pass, - &[], - ) - .await - .unwrap(); + let page_content = String::from_utf8(vec![b'o'; page_size as usize])?; + let page_links: HashMap> = HashMap::new(); + let page_path = "simulation-page"; + let page_id = U256::zero(); + + let flo_page = FloWrapper::from_type_config( + rv.realm.realm_id(), + flcrypto::access::Condition::Pass, + FloConfig { + cuckoo: flmodules::dht_storage::core::Cuckoo::None, + force_id: Some(page_id), + }, + BlobPage(Blob::make( + "re.fledg.page".into(), + page_links, + [("path".to_string(), page_path.into())].into(), + [("index.html".to_string(), page_content.into())].into(), + )), + &[], + )?; + // let flo_page = FloBlobPage::new_cuckoo( + // rv.realm.realm_id(), + // flcrypto::access::Condition::Pass, + // &format!("simulation-filler-{}", i.to_string()), + // Bytes::from(page_content), + // None, + // flmodules::dht_storage::core::Cuckoo::None.clone(), + // &[], + // )?; + // let flo_page = rv + // .create_http( + // "simulation-page", + // String::from_utf8(vec![b'o'; page_size as usize])?, + // None, + // flcrypto::access::Condition::Pass, + // &[], + // ) + // .await + // .unwrap(); let page_content = String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()).unwrap(); @@ -390,7 +432,8 @@ impl SimulationHandler { rv.set_realm_service("simulation-page", flo_page.blob_id(), &[&signer]) .await?; - //ds.store_flo(flo_page.flo().clone())?; + ds.store_flo(flo_page.flo().clone())?; + wait_ms(1000).await; ds.sync()?; @@ -430,8 +473,8 @@ impl SimulationHandler { log::info!("DHT CONNECTED"); - let ds = f.node.dht_storage.as_mut().unwrap(); - let mut rv = RealmView::new_first(ds.clone()).await?; + //let ds = f.node.dht_storage.as_mut().unwrap(); + //let mut rv = RealmView::new_first(ds.clone()).await?; loop { if start_instant.elapsed().as_millis() > timeout_ms as u128 { @@ -452,8 +495,8 @@ impl SimulationHandler { f.node.dht_router.as_ref().unwrap().stats.borrow().active as u64 ); - rv.update_all().await?; - f.node.dht_storage.as_mut().unwrap().sync()?; + //rv.update_all().await?; + //f.node.dht_storage.as_mut().unwrap().sync()?; let pages = f .node @@ -475,6 +518,7 @@ impl SimulationHandler { if page_count >= amount as usize { log::info!("enough pages received"); log::info!("SIMULATION END"); + absolute_counter!("fledger_simulation_success", 1); f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); } else { @@ -487,9 +531,16 @@ impl SimulationHandler { mut f: Fledger, sampling_rate_ms: u32, timeout_ms: u32, + experiment_id: u32, ) -> anyhow::Result<()> { let start_instant = Instant::now(); absolute_counter!("fledger_simulation_success", 0); + absolute_counter!("fledger_connected_total", 0); + absolute_counter!("fledger_forwarded_flo_requests_total", 0); + absolute_counter!("fledger_forwarded_flo_meta_requests_total", 0); + absolute_counter!("fledger_blocked_flo_requests_total", 0); + absolute_counter!("fledger_flo_value_sent_total", 0); + absolute_counter!("fledger_flo_value_blocked_total", 0); let timeout_result = timeout( Duration::from_millis(timeout_ms.into()), @@ -510,6 +561,8 @@ impl SimulationHandler { let ds = f.node.dht_storage.as_mut().unwrap(); let mut rv = RealmView::new_first(ds.clone()).await?; + let mut page_service: Option<&FloID>; + loop { if start_instant.elapsed().as_millis() > timeout_ms as u128 { log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); @@ -524,23 +577,54 @@ impl SimulationHandler { increment_counter!("fledger_iterations_total"); - rv.update_all().await?; + absolute_counter!( + "fledger_connected_total", + f.node.dht_router.as_ref().unwrap().stats.borrow().active as u64 + ); + + //ds.sync()?; + // let _ = rv + // .update_all() + // .await + // .inspect_err(|e| log::error!("error when doing rv.update_all(): {e}")); - let pages = ds.get_flos().await.unwrap().clone(); + let pages = ds + .get_flos() + .await + .unwrap_or_else(|e| { + log::error!("failed to get flos {e}"); + vec![] + }) + .clone(); let pages = pages .iter() .filter(|flo| flo.flo_type() == type_name::()) .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); - let mut simulation_page_stored_this_iteration = false; - pages.clone().for_each(|page| { - let page_name = page.0.values().iter().next().unwrap().1; - log::info!("page found {}", page_name); - if page_name == "simulation-page" { - simulation_page_stored_this_iteration = true; - } + let page_names = pages + .clone() + .map(|page| page.0.values().iter().next().unwrap().1.clone()); + + let simulation_page_stored_this_iteration = page_names + .clone() + .find(|name| name == "simulation-page") + .is_some(); + + let page_list = page_names + .clone() + .map(|name| { + return name.replace("simulation-filler-", ""); + }) + .collect::>(); + + page_list.clone().iter().for_each(|page| { + let metric = format!("fledger_page_stored_{}", page.replace("-", "_")); + absolute_counter!(metric, 1); }); + let pages_csv = page_list.clone().join(", "); + log::info!("pages stored: {pages_csv}"); + if simulation_page_stored_this_iteration { absolute_counter!("fledger_simulation_page_stored", 1); } else { @@ -552,7 +636,22 @@ impl SimulationHandler { let ds_size = ds.stats.borrow().realm_stats.iter().next().unwrap().1.size; absolute_counter!("fledger_realm_storage_bytes", ds_size as u64); - let page_id_opt = rv.realm.cache().get_services().get("simulation-page"); + // let page_id_opt = Some(FloID::from_str( + // "c9e737fc7c55f404388d3eda20d5a047adc3b50e4ac59f25c0f9d8ce23d5fb94", + // )?); + let page_id_opt: Option = None; // Testing whether pages propagate with no + // get_flo + + let page_service_unmutable = rv.realm.cache().get_services().get("simulation-page"); + page_service = page_service_unmutable.clone(); + if page_service.is_none() { + if page_service.is_some() { + log::info!("service received"); + log::info!(" - id from service: {}", page_service.clone().unwrap()); + log::info!(" - id hardcoded : {}", page_id_opt.clone().unwrap()); + } + } + if let Some(page_id) = page_id_opt { let page_global_id = GlobalID::new(rv.realm.realm_id(), page_id.clone()); let page_flo_wrapper_result: Result, Error> = @@ -588,7 +687,7 @@ impl SimulationHandler { } else { increment_counter!("fledger_simulation_page_fetch_fail_total"); - log::info!("could not fetch page with id [{}].", page_id); + //log::info!("could not fetch page with id [{}].", page_id); } } else { increment_counter!("fledger_simulation_service_fetch_fail_total"); diff --git a/deploy-binaries.sh b/deploy-binaries.sh index 0fed307b..e809d3bb 100755 --- a/deploy-binaries.sh +++ b/deploy-binaries.sh @@ -17,11 +17,17 @@ if ! test -f "target-common/x86_64-unknown-linux-musl/release/fledger"; then exit 1 fi +echo "Copy binaries for local experiments..." +cp \ + target-common/x86_64-unknown-linux-musl/release/fledger \ + target-common/x86_64-unknown-linux-musl/release/flsignal \ + /var/fledger/ + echo "Uploading fledger and flsignal binaries..." scp \ target-common/x86_64-unknown-linux-musl/release/fledger \ target-common/x86_64-unknown-linux-musl/release/flsignal \ - sphere-fledger:/usr/share/caddy/ + fledger:/usr/share/caddy/ # echo "Pushing fledger and flsignal binaries to github..." # cp \ diff --git a/flarch/src/nodeids.rs b/flarch/src/nodeids.rs index 6c1e6abd..37ea350c 100644 --- a/flarch/src/nodeids.rs +++ b/flarch/src/nodeids.rs @@ -24,7 +24,7 @@ pub struct U256(#[serde_as(as = "Hex")] [u8; 32]); impl fmt::Display for U256 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for index in 0..8 { + for index in 0..32 { f.write_fmt(format_args!("{:02x}", self.0[index]))?; } Ok(()) diff --git a/flmodules/src/dht_router/messages.rs b/flmodules/src/dht_router/messages.rs index 38ada69d..5f78c4e8 100644 --- a/flmodules/src/dht_router/messages.rs +++ b/flmodules/src/dht_router/messages.rs @@ -18,6 +18,8 @@ use super::{ kademlia::*, }; +pub static mut EVIL_NO_FORWARD: bool = false; + /// These are the messages which will be exchanged between the nodes for this /// module. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/flmodules/src/dht_storage/core.rs b/flmodules/src/dht_storage/core.rs index d3cafa1d..2b997782 100644 --- a/flmodules/src/dht_storage/core.rs +++ b/flmodules/src/dht_storage/core.rs @@ -260,11 +260,11 @@ impl RealmStorage { self.realm_config.max_space ); } - log::warn!( - "realm storage : current({}) / max({})", - self.size, - self.realm_config.max_space - ); + // log::warn!( + // "realm storage : current({}) / max({})", + // self.size, + // self.realm_config.max_space + // ); is_new_flo } diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 0c2bf22f..0077e080 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{any::type_name, collections::HashMap}; use flarch::{ broker::SubsystemHandler, @@ -226,10 +226,16 @@ impl Messages { .and_then(|realm| realm.get_flo_cuckoo(&fid)) { // log::info!("sends flo {:?}", fc.0); - return MessageDest::FloValue(fc) - .to_intern_out(origin) - // .inspect(|msg| log::info!("{} sends {msg:?}", self.our_id)) - .map_or(vec![], |msg| vec![msg]); + if unsafe { !EVIL_NO_FORWARD } { + increment_counter!("fledger_flo_value_sent_total"); + return MessageDest::FloValue(fc) + .to_intern_out(origin) + // .inspect(|msg| log::info!("{} sends {msg:?}", self.our_id)) + .map_or(vec![], |msg| vec![msg]); + } else { + increment_counter!("fledger_flo_value_blocked_total"); + return vec![]; + } } } MessageClosest::GetCuckooIDs(rid) => { @@ -308,8 +314,21 @@ impl Messages { ) .collect() } + MessageNeighbour::RequestFloMetas(realm_id) => { + if unsafe { !EVIL_NO_FORWARD } { + increment_counter!("fledger_forwarded_flo_meta_requests_total"); + self.realms + .get(&realm_id) + .map(|realm| realm.get_flo_metas()) + .map_or(vec![], |fm| { + vec![MessageNeighbour::AvailableFlos(realm_id, fm)] + }) + } else { + increment_counter!("fledger_blocked_flo_meta_requests_total"); + vec![] + } + } MessageNeighbour::AvailableFlos(realm_id, flo_metas) => { - log::info!("AvailableFlos: {}", flo_metas.len()); absolute_counter!( "fledger_flos_metas_received_from_neighbour", flo_metas.len() as u64 @@ -318,7 +337,7 @@ impl Messages { .get(&realm_id) .and_then(|realm| realm.sync_available(&flo_metas)) .map_or(vec![], |needed| { - log::info!("Needed flos: {}", needed.len()); + //log::info!("Needed flos: {}", needed.len()); absolute_counter!( "fledger_flos_requested_from_neighbour", needed.len() as u64 @@ -326,22 +345,7 @@ impl Messages { vec![MessageNeighbour::RequestFlos(realm_id, needed)] }) } - MessageNeighbour::RequestFloMetas(realm_id) => { - if unsafe { !EVIL_NO_FORWARD } { - increment_counter!("fledger_forwarded_flo_meta_requests_total"); - self.realms - .get(&realm_id) - .map(|realm| realm.get_flo_metas()) - .map_or(vec![], |fm| { - vec![MessageNeighbour::AvailableFlos(realm_id, fm)] - }) - } else { - increment_counter!("fledger_blocked_flo_meta_requests_total"); - vec![] - } - } MessageNeighbour::RequestFlos(realm_id, flo_ids) => { - log::info!("RequestFlos: {flo_ids:?}"); absolute_counter!( "fledger_flos_ids_received_from_neighbour", // requested flo_ids.len() as u64 @@ -351,14 +355,24 @@ impl Messages { self.realms .get(&realm_id) .map(|realm| { - flo_ids + let flos = flo_ids .iter() - .take(1) // TODO: remove .filter_map(|id| realm.get_flo_cuckoo(id)) - .collect::>() + .collect::>(); + + let realm = flos + .iter() + .find(|flo| flo.0.flo_type() == type_name::()); + + let flo_to_forward = realm.or(flos.last()); + if let Some(flo) = flo_to_forward { + return vec![flo.clone()]; + } else { + return vec![]; + } }) .map_or(vec![], |flos| { - log::info!("Flos to send: {}", flos.len()); + //log::info!("Flos to send: {}", flos.len()); absolute_counter!("fledger_flos_sent_to_neighbour", flos.len() as u64); vec![MessageNeighbour::Flos(flos)] }) @@ -368,7 +382,6 @@ impl Messages { } } MessageNeighbour::Flos(flo_cuckoos) => { - log::info!("Flos: {}", flo_cuckoos.len()); absolute_counter!( "fledger_flos_received_from_neighbour", // received flo_cuckoos.len() as u64 @@ -417,14 +430,14 @@ impl Messages { // Either its realm is already known, or it is a new realm. // When 'true' is returned, then the flo has been stored. fn upsert_flo(&mut self, flo: Flo) -> bool { - log::info!( - "{} store_flo {}({}/{}) {}", - self.our_id, - flo.flo_type(), - flo.flo_id(), - flo.realm_id(), - flo.version() - ); + // log::info!( + // "{} store_flo {}({}/{}) {}", + // self.our_id, + // flo.flo_type(), + // flo.flo_id(), + // flo.realm_id(), + // flo.version() + // ); // log::info!( // "{} has realm: {}", // self.our_id, diff --git a/flmodules/src/flo/blob.rs b/flmodules/src/flo/blob.rs index fd18c18a..ce6be70f 100644 --- a/flmodules/src/flo/blob.rs +++ b/flmodules/src/flo/blob.rs @@ -199,6 +199,20 @@ impl Blob { datas: HashMap::new(), } } + + pub fn make( + blob_type: String, + links: HashMap>, + values: HashMap, + datas: HashMap, + ) -> Self { + Self { + blob_type, + links, + values, + datas, + } + } } impl BlobFamily for FloBlob {} From 57cae03ddc1e01738e80ec140eae88e9bb4c8b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sat, 7 Jun 2025 23:51:04 +0200 Subject: [PATCH 31/43] feat: upload to dashboard --- cli/fledger/Cargo.toml | 2 ++ cli/fledger/src/simulation.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/cli/fledger/Cargo.toml b/cli/fledger/Cargo.toml index aed80cc3..48a21e91 100644 --- a/cli/fledger/Cargo.toml +++ b/cli/fledger/Cargo.toml @@ -28,3 +28,5 @@ webrtc-util = "0.10" metrics = "0.21.1" metrics-exporter-influx = { version = "0.2.2", path = "../../metrics-exporter-influx" } rand = "0.9.1" + +reqwest = { version = "0.12.19", features = ["json"] } diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index f25415b1..7771e0ad 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -625,6 +625,22 @@ impl SimulationHandler { let pages_csv = page_list.clone().join(", "); log::info!("pages stored: {pages_csv}"); + let mut data = HashMap::new(); + data.insert("pages", pages_csv.clone()); + let node_name = f.node.node_config.info.name.clone(); + let response = reqwest::Client::new() + .post(format!( + "https://fledger.yohan.ch/api/experiments/{experiment_id}/nodes/{node_name}" + )) + .json(&data) + .header("Accept", "application/json") + .header( + "Authorization", + "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", + ) + .send() + .await?; + if simulation_page_stored_this_iteration { absolute_counter!("fledger_simulation_page_stored", 1); } else { From 70860c5315bd53eb39daed90292ed5cd6460be78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Sun, 8 Jun 2025 21:37:38 +0200 Subject: [PATCH 32/43] feat: better upload to dashboard --- Cargo.lock | 3 + cli/fledger/Cargo.toml | 8 +- cli/fledger/src/main.rs | 8 +- cli/fledger/src/metrics.rs | 157 +++++++++++++++++++++++++- cli/fledger/src/simulation.rs | 62 +++++----- cli/flsignal/src/main.rs | 2 +- flmodules/src/dht_storage/messages.rs | 73 +++++++++++- 7 files changed, 271 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0306794f..7722c60b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1291,6 +1291,8 @@ dependencies = [ "metrics", "metrics-exporter-influx", "rand 0.9.1", + "reqwest", + "serde_json", "thiserror 2.0.12", "tokio", "webrtc-util", @@ -3063,6 +3065,7 @@ dependencies = [ "async-compression", "base64 0.22.1", "bytes", + "futures-channel", "futures-core", "futures-util", "http 1.3.1", diff --git a/cli/fledger/Cargo.toml b/cli/fledger/Cargo.toml index 48a21e91..ae8b8da5 100644 --- a/cli/fledger/Cargo.toml +++ b/cli/fledger/Cargo.toml @@ -28,5 +28,11 @@ webrtc-util = "0.10" metrics = "0.21.1" metrics-exporter-influx = { version = "0.2.2", path = "../../metrics-exporter-influx" } rand = "0.9.1" +serde_json = "1.0.140" -reqwest = { version = "0.12.19", features = ["json"] } +#openssl = { version = "0.10.73", features = ["vendored"] } +reqwest = { version = "0.12.19", features = [ + "json", + "rustls-tls", + "blocking", +], default-features = false } diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 2c191d84..87eadaa4 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -95,9 +95,9 @@ pub struct Args { #[arg(long, default_value = "false")] evil_noforward: bool, - /// Sampling rate for metrics + /// Delay between loop iterations in ms #[arg(long, default_value = "1000")] - sampling_rate_ms: u32, + loop_delay: u32, #[command(subcommand)] command: Option, @@ -147,7 +147,7 @@ async fn main() -> anyhow::Result<()> { // necessary to grab the variable for lifetime purposes. let node_name = args.name.clone().unwrap_or("unknown".into()); - let _influx = Metrics::setup(node_name, args.sampling_rate_ms); + let _influx = Metrics::setup(node_name); log::info!( "Starting app with version {}/{}", @@ -224,7 +224,7 @@ impl Fledger { Commands::Stats {} => todo!(), Commands::Page { command } => Page::run(f, command).await, Commands::Simulation(command) => { - SimulationHandler::run(f, command, args.sampling_rate_ms).await + SimulationHandler::run(f, command, args.loop_delay).await } }, None => f.loop_node(FledgerState::Forever).await, diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs index 647d3b97..ae9e92e3 100644 --- a/cli/fledger/src/metrics.rs +++ b/cli/fledger/src/metrics.rs @@ -1,19 +1,168 @@ -use std::{fs::File, time::Duration}; +use std::{collections::HashMap, fs::File, time::Duration}; +use anyhow::Error; +use flmodules::dht_storage::messages::ExperimentStats; use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; +use serde_json::Value; -pub struct Metrics {} +#[derive(Clone)] +pub struct Metrics { + experiment_id: u32, + node_name: String, + node_id: Option, +} impl Metrics { - pub fn setup(node_name: String, sampling_rate_ms: u32) -> InfluxRecorderHandle { + pub fn new(experiment_id: u32, node_name: String) -> Self { + let mut metrics = Self { + experiment_id, + node_name, + node_id: None, + }; + + let _ = metrics.api_create_node(); + return metrics.clone(); + } + + pub fn setup(node_name: String) -> InfluxRecorderHandle { log::info!("Setting up metrics"); let metrics_file = File::create(format!("/tmp/{}.metrics", node_name)) .expect(format!("could not create /tmp/{}.metrics", node_name).as_ref()); return InfluxBuilder::new() - .with_duration(Duration::from_millis(sampling_rate_ms.into())) + .with_duration(Duration::from_secs(10)) .with_writer(metrics_file) .add_global_tag("node_name", node_name) .install() .expect("could not setup influx recorder"); } + + pub fn update(&self, pages_csv: String, experiment_stats: ExperimentStats) { + let mut data = HashMap::new(); + data.insert("pages", pages_csv.clone()); + data.insert( + "amount_flo_value_sent", + experiment_stats.amount_flo_value_sent().to_string(), + ); + data.insert( + "amount_request_flo_metas_received", + experiment_stats + .amount_request_flo_metas_received() + .to_string(), + ); + self.api_put_data(data); + + let mut datapoints = HashMap::new(); + datapoints.insert( + "amount_request_flo_metas_received", + experiment_stats + .amount_request_flo_metas_received() + .to_string(), + ); + self.api_post_datapoints(datapoints); + + let mut timeless_datapoints = HashMap::new(); + timeless_datapoints.insert( + "amount_flo_value_sent", + experiment_stats.amount_flo_value_sent().to_string(), + ); + timeless_datapoints.insert( + "amount_request_flo_metas_received", + experiment_stats + .amount_request_flo_metas_received() + .to_string(), + ); + self.api_post_timeless_datapoints(timeless_datapoints); + } + + pub fn timeout(&self) { + let mut data = HashMap::new(); + data.insert("status", "timeout".to_string()); + self.api_put_data(data); + } + + pub fn success(&self) { + let mut data = HashMap::new(); + data.insert("status", "success".to_string()); + self.api_put_data(data); + } + + pub fn api_put_data(&self, data: HashMap<&str, String>) { + match reqwest::blocking::Client::new() + .put(format!( + "https://fledger.yohan.ch/api/nodes/{}", + self.node_id.clone().unwrap() + )) + .json(&data) + .header("Accept", "application/json") + .header( + "Authorization", + "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", + ) + .send() + { + Ok(resp) => log::info!("Successful API request: {}", resp.text().unwrap()), + Err(err) => log::error!("Error: {}", err), + }; + } + + pub fn api_post_timeless_datapoints(&self, data: HashMap<&str, String>) { + self.api_post_nodes("timeless-data-points".to_string(), data); + } + + pub fn api_post_datapoints(&self, data: HashMap<&str, String>) { + self.api_post_nodes("data-points".to_string(), data); + } + + pub fn api_post_nodes(&self, resource: String, data: HashMap<&str, String>) { + match reqwest::blocking::Client::new() + .post(format!( + "https://fledger.yohan.ch/api/nodes/{}/{resource}", + self.node_id.clone().unwrap() + )) + .json(&data) + .header("Accept", "application/json") + .header( + "Authorization", + "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", + ) + .send() + { + Ok(resp) => log::info!( + "Successful API request ({resource}): {}", + resp.text().unwrap() + ), + Err(err) => log::error!("Error: {}", err), + }; + } + + fn api_create_node(&mut self) -> Result<(), Error> { + let mut data = HashMap::new(); + data.insert("name", self.node_name.clone()); + + match reqwest::blocking::Client::new() + .post(format!( + "https://fledger.yohan.ch/api/experiments/{}/nodes/", + self.experiment_id + )) + .json(&data) + .header("Accept", "application/json") + .header( + "Authorization", + "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", + ) + .send() + { + Ok(resp) => { + let text = resp.text().unwrap(); + log::info!("Successfully created node in API: {}", text.clone()); + let response_data: Value = serde_json::from_str(&text.clone())?; + self.node_id = Some(response_data["id"].to_string()); + return Ok(()); + } + Err(err) => { + log::error!("Error: {}", err); + return Err(err.into()); + } + }; + } } diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 7771e0ad..348f6cc5 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,6 +1,6 @@ use std::{any::type_name, collections::HashMap, str::FromStr, time::Duration, vec}; -use crate::Fledger; +use crate::{metrics::Metrics, Fledger}; use anyhow::Error; use clap::{arg, Args, Subcommand}; use flarch::{nodeids::U256, tasks::wait_ms}; @@ -69,6 +69,9 @@ pub enum SimulationSubcommand { #[arg(long, default_value = "20000")] timeout_ms: u32, + #[arg(long, default_value = "false")] + enable_sync: bool, + #[arg(long)] experiment_id: u32, }, @@ -113,10 +116,17 @@ impl SimulationHandler { } SimulationSubcommand::FetchPage { timeout_ms, + enable_sync, experiment_id, } => { - Self::run_dht_fetch_simulation_page(f, sampling_rate_ms, timeout_ms, experiment_id) - .await + Self::run_dht_fetch_simulation_page( + f, + sampling_rate_ms, + enable_sync, + timeout_ms, + experiment_id, + ) + .await } SimulationSubcommand::WaitPages { timeout_ms, amount } => { Self::run_dht_wait_for_pages(f, sampling_rate_ms, timeout_ms, amount).await @@ -530,6 +540,7 @@ impl SimulationHandler { async fn run_dht_fetch_simulation_page( mut f: Fledger, sampling_rate_ms: u32, + enable_sync: bool, timeout_ms: u32, experiment_id: u32, ) -> anyhow::Result<()> { @@ -542,6 +553,8 @@ impl SimulationHandler { absolute_counter!("fledger_flo_value_sent_total", 0); absolute_counter!("fledger_flo_value_blocked_total", 0); + let metrics = Metrics::new(experiment_id, f.node.node_config.info.name.clone()); + let timeout_result = timeout( Duration::from_millis(timeout_ms.into()), f.loop_node(crate::FledgerState::DHTAvailable), @@ -552,28 +565,34 @@ impl SimulationHandler { log::warn!("SIMULATION TIMEOUT WHILE CONNECTING TO DHT"); log::info!("SIMULATION END"); absolute_counter!("fledger_simulation_timeout", 1); + metrics.timeout(); + return Err(timeout_result.unwrap_err().into()); } absolute_counter!("fledger_dht_connected", 1); log::info!("DHT CONNECTED"); - let ds = f.node.dht_storage.as_mut().unwrap(); - let mut rv = RealmView::new_first(ds.clone()).await?; + let mut rv = RealmView::new_first(f.node.dht_storage.as_ref().unwrap().clone()).await?; let mut page_service: Option<&FloID>; + let mut iteration = 0 as u32; loop { if start_instant.elapsed().as_millis() > timeout_ms as u128 { log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); log::info!("SIMULATION END"); absolute_counter!("fledger_simulation_timeout", 1); + metrics.timeout(); f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); } + let ds = f.node.dht_storage.as_mut().unwrap(); + wait_ms(sampling_rate_ms.into()).await; + iteration += 1; increment_counter!("fledger_iterations_total"); @@ -582,7 +601,9 @@ impl SimulationHandler { f.node.dht_router.as_ref().unwrap().stats.borrow().active as u64 ); - //ds.sync()?; + if enable_sync { + ds.sync()?; + } // let _ = rv // .update_all() // .await @@ -617,29 +638,13 @@ impl SimulationHandler { }) .collect::>(); - page_list.clone().iter().for_each(|page| { - let metric = format!("fledger_page_stored_{}", page.replace("-", "_")); - absolute_counter!(metric, 1); - }); - let pages_csv = page_list.clone().join(", "); log::info!("pages stored: {pages_csv}"); - let mut data = HashMap::new(); - data.insert("pages", pages_csv.clone()); - let node_name = f.node.node_config.info.name.clone(); - let response = reqwest::Client::new() - .post(format!( - "https://fledger.yohan.ch/api/experiments/{experiment_id}/nodes/{node_name}" - )) - .json(&data) - .header("Accept", "application/json") - .header( - "Authorization", - "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", - ) - .send() - .await?; + if iteration % 10 == 0 { + let ds_stats = ds.stats.borrow().experiment_stats.clone(); + metrics.update(pages_csv.clone(), ds_stats); + } if simulation_page_stored_this_iteration { absolute_counter!("fledger_simulation_page_stored", 1); @@ -697,6 +702,11 @@ impl SimulationHandler { log::info!("SIMULATION END"); absolute_counter!("fledger_simulation_success", 1); + metrics.update( + pages_csv.clone(), + ds.stats.borrow().experiment_stats.clone(), + ); + metrics.success(); f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); diff --git a/cli/flsignal/src/main.rs b/cli/flsignal/src/main.rs index bbfaef0a..0204f58d 100644 --- a/cli/flsignal/src/main.rs +++ b/cli/flsignal/src/main.rs @@ -30,7 +30,7 @@ fn setup_metrics(node_name: String) -> InfluxRecorderHandle { let metrics_file = File::create(format!("/tmp/{}.metrics", node_name)) .expect(format!("could not create /tmp/{}.metrics", node_name).as_ref()); return InfluxBuilder::new() - .with_duration(Duration::from_secs(1)) + .with_duration(Duration::from_secs(10)) .with_writer(metrics_file) .add_global_tag("node_name", node_name) .install() diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 0077e080..58333621 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -88,10 +88,17 @@ pub struct RealmStats { pub config: RealmConfig, } +#[derive(Debug, Default, Clone)] +pub struct ExperimentStats { + amount_flo_value_sent: u32, + amount_request_flo_metas_received: u32, +} + #[derive(Debug, Default, Clone)] pub struct Stats { pub realm_stats: HashMap, pub system_realms: Vec, + pub experiment_stats: ExperimentStats, } /// The message handling part, but only for DHTStorage messages. @@ -102,6 +109,8 @@ pub struct Messages { our_id: NodeID, ds: Box, tx: Option>, + + experiment_stats: ExperimentStats, } pub static mut EVIL_NO_FORWARD: bool = false; @@ -123,6 +132,8 @@ impl Messages { nodes: vec![], ds, tx: Some(tx), + + experiment_stats: ExperimentStats::new(), }; msgs.store(); (msgs, rx) @@ -228,6 +239,7 @@ impl Messages { // log::info!("sends flo {:?}", fc.0); if unsafe { !EVIL_NO_FORWARD } { increment_counter!("fledger_flo_value_sent_total"); + self.incr_flo_value_sent(); return MessageDest::FloValue(fc) .to_intern_out(origin) // .inspect(|msg| log::info!("{} sends {msg:?}", self.our_id)) @@ -317,6 +329,7 @@ impl Messages { MessageNeighbour::RequestFloMetas(realm_id) => { if unsafe { !EVIL_NO_FORWARD } { increment_counter!("fledger_forwarded_flo_meta_requests_total"); + self.incr_request_flo_metas_received(); self.realms .get(&realm_id) .map(|realm| realm.get_flo_metas()) @@ -485,15 +498,33 @@ impl Messages { } fn store(&mut self) { - self.tx.clone().map(|tx| { - tx.send(Stats::from_realms(&self.realms, self.config.realms.clone())) - .is_err() - .then(|| self.tx = None) - }); + self.refresh_stats(); serde_yaml::to_string(&self.realms) .ok() .map(|s| (*self.ds).set(MODULE_NAME, &s)); } + + fn incr_request_flo_metas_received(&mut self) { + self.experiment_stats.incr_request_flo_metas_received(); + self.refresh_stats(); + } + + fn incr_flo_value_sent(&mut self) { + self.experiment_stats.incr_flo_value_sent(); + self.refresh_stats(); + } + + fn refresh_stats(&mut self) { + self.tx.clone().map(|tx| { + tx.send(Stats::from_realms( + &self.realms, + self.config.realms.clone(), + self.experiment_stats.clone(), + )) + .is_err() + .then(|| self.tx = None) + }); + } } #[platform_async_trait()] @@ -517,14 +548,44 @@ impl SubsystemHandler for Messages { } } +impl ExperimentStats { + fn new() -> Self { + Self { + amount_flo_value_sent: 0, + amount_request_flo_metas_received: 0, + } + } + + pub fn amount_flo_value_sent(&self) -> u32 { + self.amount_flo_value_sent + } + + pub fn amount_request_flo_metas_received(&self) -> u32 { + self.amount_request_flo_metas_received + } + + fn incr_flo_value_sent(&mut self) { + self.amount_flo_value_sent += 1; + } + + fn incr_request_flo_metas_received(&mut self) { + self.amount_request_flo_metas_received += 1; + } +} + impl Stats { - fn from_realms(realms: &HashMap, system_realms: Vec) -> Self { + fn from_realms( + realms: &HashMap, + system_realms: Vec, + experiment_stats: ExperimentStats, + ) -> Self { Self { realm_stats: realms .iter() .map(|(id, realm)| (id.clone(), RealmStats::from_realm(realm))) .collect(), system_realms, + experiment_stats, } } } From dbb27222275396863c9080e52aa218eb1f0171f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 9 Jun 2025 14:10:05 +0200 Subject: [PATCH 33/43] feat: cleanup metrics and simulation files structure --- cli/fledger/src/main.rs | 5 +- cli/fledger/src/metrics.rs | 77 +- cli/fledger/src/simulation.rs | 681 +----------------- cli/fledger/src/simulation_chat/mod.rs | 1 + cli/fledger/src/simulation_chat/simulation.rs | 80 ++ cli/fledger/src/simulation_dht_target/mod.rs | 2 + .../src/simulation_dht_target/simulation.rs | 278 +++++++ .../src/simulation_dht_target/stats.rs | 167 +++++ cli/fledger/src/simulation_realm/mod.rs | 1 + .../src/simulation_realm/simulation.rs | 17 + flmodules/src/dht_storage/messages.rs | 138 ++-- flmodules/src/dht_storage/realm_view.rs | 6 +- flmodules/src/flo/flo.rs | 2 +- flmodules/src/gossip_events/broker.rs | 2 +- flmodules/src/gossip_events/messages.rs | 4 +- flmodules/src/template/messages.rs | 2 +- flmodules/src/web_proxy/messages.rs | 2 +- flnode/src/node.rs | 2 +- 18 files changed, 717 insertions(+), 750 deletions(-) create mode 100644 cli/fledger/src/simulation_chat/mod.rs create mode 100644 cli/fledger/src/simulation_chat/simulation.rs create mode 100644 cli/fledger/src/simulation_dht_target/mod.rs create mode 100644 cli/fledger/src/simulation_dht_target/simulation.rs create mode 100644 cli/fledger/src/simulation_dht_target/stats.rs create mode 100644 cli/fledger/src/simulation_realm/mod.rs create mode 100644 cli/fledger/src/simulation_realm/simulation.rs diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 87eadaa4..b047ebd6 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -1,6 +1,5 @@ use clap::{Parser, Subcommand}; -use ::metrics::absolute_counter; use flarch::{ data_storage::{DataStorage, DataStorageFile}, random, @@ -13,6 +12,7 @@ use flmodules::{ network::{broker::NetworkIn, network_start, signal::SIGNAL_VERSION}, }; use flnode::{node::Node, version::VERSION_STRING}; +use ::metrics::absolute_counter; use metrics::Metrics; use page::{Page, PageCommands}; use realm::{RealmCommands, RealmHandler}; @@ -22,6 +22,9 @@ mod metrics; mod page; mod realm; mod simulation; +mod simulation_chat; +mod simulation_dht_target; +mod simulation_realm; /// Fledger node CLI binary #[derive(Parser, Debug, Clone)] diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs index ae9e92e3..1e0ddc16 100644 --- a/cli/fledger/src/metrics.rs +++ b/cli/fledger/src/metrics.rs @@ -1,10 +1,11 @@ use std::{collections::HashMap, fs::File, time::Duration}; use anyhow::Error; -use flmodules::dht_storage::messages::ExperimentStats; use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; use serde_json::Value; +use crate::simulation_dht_target::stats::SimulationStats; + #[derive(Clone)] pub struct Metrics { experiment_id: u32, @@ -36,39 +37,69 @@ impl Metrics { .expect("could not setup influx recorder"); } - pub fn update(&self, pages_csv: String, experiment_stats: ExperimentStats) { + pub fn upload(&self, simulation_metrics: SimulationStats) { let mut data = HashMap::new(); - data.insert("pages", pages_csv.clone()); - data.insert( - "amount_flo_value_sent", - experiment_stats.amount_flo_value_sent().to_string(), - ); - data.insert( - "amount_request_flo_metas_received", - experiment_stats - .amount_request_flo_metas_received() - .to_string(), - ); + data.insert("pages", simulation_metrics.pages.clone()); self.api_put_data(data); let mut datapoints = HashMap::new(); datapoints.insert( - "amount_request_flo_metas_received", - experiment_stats - .amount_request_flo_metas_received() - .to_string(), + "connected_nodes_total", + simulation_metrics.connected_nodes_total.to_string(), ); self.api_post_datapoints(datapoints); let mut timeless_datapoints = HashMap::new(); timeless_datapoints.insert( - "amount_flo_value_sent", - experiment_stats.amount_flo_value_sent().to_string(), + "target_page_stored_bool", + simulation_metrics.target_page_stored_bool.to_string(), + ); + timeless_datapoints.insert( + "connected_nodes_total", + simulation_metrics.connected_nodes_total.to_string(), + ); + timeless_datapoints.insert( + "pages_stored_total", + simulation_metrics.pages_stored_total.to_string(), + ); + timeless_datapoints.insert( + "ds_size_bytes", + simulation_metrics.ds_size_bytes.to_string(), + ); + + timeless_datapoints.insert( + "flos_sent_total", + simulation_metrics + .ds_experiment_stats + .max_flos_sent_in_flos + .to_string(), + ); + timeless_datapoints.insert( + "available_flos_sent_total", + simulation_metrics + .ds_experiment_stats + .flo_value_sent_total + .to_string(), + ); + timeless_datapoints.insert( + "available_flos_sent_blocked_total", + simulation_metrics + .ds_experiment_stats + .flo_value_sent_blocked_total + .to_string(), + ); + timeless_datapoints.insert( + "max_flo_metas_received_in_available_flos", + simulation_metrics + .ds_experiment_stats + .max_flo_metas_received_in_available_flos + .to_string(), ); timeless_datapoints.insert( - "amount_request_flo_metas_received", - experiment_stats - .amount_request_flo_metas_received() + "max_flo_metas_requested_in_request_flos", + simulation_metrics + .ds_experiment_stats + .max_flo_metas_requested_in_request_flos .to_string(), ); self.api_post_timeless_datapoints(timeless_datapoints); @@ -77,12 +108,14 @@ impl Metrics { pub fn timeout(&self) { let mut data = HashMap::new(); data.insert("status", "timeout".to_string()); + //absolute_counter!("fledger_simulation_timeout", 1); self.api_put_data(data); } pub fn success(&self) { let mut data = HashMap::new(); data.insert("status", "success".to_string()); + //absolute_counter!("fledger_simulation_success", 1); self.api_put_data(data); } diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 348f6cc5..d7e69b8d 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,27 +1,13 @@ -use std::{any::type_name, collections::HashMap, str::FromStr, time::Duration, vec}; - -use crate::{metrics::Metrics, Fledger}; -use anyhow::Error; +use crate::simulation_chat::simulation::SimulationChat; +use crate::simulation_realm::simulation::SimulationRealm; +use crate::{simulation_dht_target::simulation::SimulationDhtTarget, Fledger}; use clap::{arg, Args, Subcommand}; -use flarch::{nodeids::U256, tasks::wait_ms}; -use flcrypto::tofrombytes::ToFromBytes; -use flmodules::{ - dht_storage::{core::FloConfig, realm_view::RealmView}, - flo::{ - blob::{Blob, BlobAccess, BlobID, BlobPage, BlobTag}, - flo::{FloID, FloWrapper}, - realm::GlobalID, - }, - gossip_events::core::Event, -}; -use metrics::{absolute_counter, increment_counter}; -use tokio::time::{timeout, Instant}; #[derive(Args, Debug, Clone)] pub struct SimulationCommand { /// Print new messages as they come #[arg(long, default_value = "false")] - print_new_messages: bool, + pub print_new_messages: bool, #[command(subcommand)] pub subcommand: SimulationSubcommand, @@ -30,11 +16,11 @@ pub struct SimulationCommand { #[derive(Subcommand, Debug, Clone)] pub enum SimulationSubcommand { Chat { - /// Send a chat message upon node creation + /// Send a simulation_chat message upon node creation #[arg(long)] send_msg: Option, - /// Wait for a chat message with the given body. + /// Wait for a simulation_chat message with the given body. /// log "RECV_CHAT_MSG TRIGGERED" upon message received, at log level info #[arg(long)] recv_msg: Option, @@ -42,17 +28,7 @@ pub enum SimulationSubcommand { DhtJoinRealm {}, - CreateTag { - #[arg(long)] - tag: String, - }, - - FetchTag { - #[arg(long)] - tag: String, - }, - - CreatePageWithFillers { + DhtCreateFillersAndTarget { #[arg(long)] filler_amount: u32, @@ -64,8 +40,12 @@ pub enum SimulationSubcommand { #[arg(long)] connection_delay: u32, + + #[arg(long)] + experiment_id: u32, }, - FetchPage { + + DhtFetchTarget { #[arg(long, default_value = "20000")] timeout_ms: u32, @@ -75,13 +55,6 @@ pub enum SimulationSubcommand { #[arg(long)] experiment_id: u32, }, - WaitPages { - #[arg(long, default_value = "20000")] - timeout_ms: u32, - - #[arg(long, default_value = "10")] - amount: u32, - }, } pub struct SimulationHandler {} @@ -90,654 +63,44 @@ impl SimulationHandler { pub async fn run( f: Fledger, command: SimulationCommand, - sampling_rate_ms: u32, + loop_delay: u32, ) -> anyhow::Result<()> { match command.subcommand.clone() { SimulationSubcommand::Chat { send_msg, recv_msg } => { - Self::run_chat(f, command, send_msg, recv_msg).await + SimulationChat::run_chat(f, command, send_msg, recv_msg).await } - SimulationSubcommand::DhtJoinRealm {} => Self::run_dht_join_realm(f).await, - SimulationSubcommand::CreateTag { tag } => Self::run_dht_create_tag(f, tag).await, - SimulationSubcommand::FetchTag { tag } => Self::run_dht_fetch_tag(f, tag).await, - SimulationSubcommand::CreatePageWithFillers { + SimulationSubcommand::DhtJoinRealm {} => SimulationRealm::run_dht_join_realm(f).await, + SimulationSubcommand::DhtCreateFillersAndTarget { filler_amount, page_size, pages_propagation_delay, connection_delay, + experiment_id, } => { - Self::run_dht_create_page_with_fillers( + SimulationDhtTarget::run_create_fillers_and_target( f, filler_amount, page_size, pages_propagation_delay, connection_delay, + experiment_id, ) .await } - SimulationSubcommand::FetchPage { + SimulationSubcommand::DhtFetchTarget { timeout_ms, enable_sync, experiment_id, } => { - Self::run_dht_fetch_simulation_page( + SimulationDhtTarget::fetch_target( f, - sampling_rate_ms, + loop_delay, enable_sync, timeout_ms, experiment_id, ) .await } - SimulationSubcommand::WaitPages { timeout_ms, amount } => { - Self::run_dht_wait_for_pages(f, sampling_rate_ms, timeout_ms, amount).await - } - } - } - - async fn run_chat( - mut f: Fledger, - simulation_args: SimulationCommand, - send_msg: Option, - recv_msg: Option, - ) -> anyhow::Result<()> { - f.loop_node(crate::FledgerState::Connected(1)).await?; - - if let Some(ref msg) = recv_msg { - log::info!("Waiting for chat message {}.", msg); - } - - if let Some(ref msg) = send_msg { - log::info!("Sending chat message {}.", msg); - f.node.add_chat_message(msg.into()).await?; - } - - let mut acked_msg_ids: Vec = Vec::new(); - - loop { - wait_ms(1000).await; - - let fledger_message_total = f.node.gossip.as_ref().unwrap().chat_events().len(); - let fledger_connected_total = f.node.nodes_connected()?.len(); - absolute_counter!( - "fledger_message_total", - fledger_message_total.try_into().unwrap() - ); - absolute_counter!("fledger_connected_total", fledger_connected_total as u64); - increment_counter!("fledger_iterations_total"); - - if simulation_args.print_new_messages { - Self::log_new_messages(&f, &mut acked_msg_ids); - } - - if let Some(ref msg) = recv_msg { - let gossip = f.node.gossip.as_ref(); - if gossip - .unwrap() - .chat_events() - .iter() - .any(|ev| ev.msg.eq(msg)) - { - log::info!("SIMULATION END"); - f.loop_node(crate::FledgerState::Forever).await?; - return Ok(()); - } - } - } - } - - async fn run_dht_join_realm(mut f: Fledger) -> anyhow::Result<()> { - f.loop_node(crate::FledgerState::DHTAvailable).await?; - log::info!("SIMULATION END"); - - absolute_counter!("fledger_realms_total", 1); - - f.loop_node(crate::FledgerState::Forever).await?; - return Ok(()); - } - - async fn run_dht_create_tag(mut f: Fledger, tag: String) -> anyhow::Result<()> { - f.loop_node(crate::FledgerState::DHTAvailable).await?; - absolute_counter!("fledger_dht_connected", 1); - - log::info!("DHT CONNECTED"); - - //let router = f.node.dht_router.unwrap(); - let ds = f.node.dht_storage.as_mut().unwrap(); - let mut rv = RealmView::new_first(ds.clone()).await?; - - // Send a Flo tag blob - log::info!("Storing tag in DHT {}.", tag); - let flo_tag = rv - .create_tag(&tag, None, flcrypto::access::Condition::Pass, &[]) - .unwrap(); - log::info!( - "tag {}/{}/{} | {}", - flo_tag.flo_id(), - flo_tag.realm_id(), - flo_tag.version(), - flo_tag.values().iter().next().unwrap().1, - ); - - let _ = ds.store_flo(flo_tag.flo().clone()); - let _ = ds.propagate(); - ds.broker.settle(Vec::new()).await?; - - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_end", 1); - - ds.get_flos().await.unwrap().iter().for_each(|flo| { - let flo_type = type_name::(); - if flo.flo_type() == flo_type { - let tag = BlobTag::from_rmp_bytes(flo_type, &flo.data()).unwrap(); - log::info!( - "tag found {}/{}/{} | {}", - flo.flo_id(), - flo.realm_id(), - flo.version(), - tag.0.values().iter().next().unwrap().1, - ) - } - }); - - let tags = rv.tags; - - log::info!("storage amt: {}", tags.storage.iter().count()); - - let tagname = tags - .storage - .iter() - .next() - .unwrap() - .1 // first tag stored - .values() - .iter() - .next() - .unwrap() - .1; // name of tag - log::info!("tag found: {}", tagname); - - f.loop_node(crate::FledgerState::Forever).await?; - return Ok(()); - } - - async fn run_dht_fetch_tag(mut f: Fledger, tag: String) -> anyhow::Result<()> { - f.loop_node(crate::FledgerState::DHTAvailable).await?; - absolute_counter!("fledger_dht_connected", 1); - - log::info!("DHT CONNECTED"); - - let ds = f.node.dht_storage.as_mut().unwrap(); - let mut rv = RealmView::new_first(ds.clone()).await?; - - loop { - wait_ms(1000).await; - - // let fledger_connected_total = f.node.nodes_connected()?.len(); // TODO: does not - // compile. - //absolute_counter!("fledger_connected_total", fledger_connected_total as u64); - increment_counter!("fledger_iterations_total"); - - rv.update_tags().await?; - - let flos = ds.get_flos().await.unwrap().clone(); - // flos.iter().for_each(|flo| { - // log::info!( - // "flo found {}/{}/{} [{}]", - // flo.flo_id(), - // flo.realm_id(), - // flo.version(), - // flo.flo_type(), - // ) - // }); - - let mut tags = flos - .iter() - .filter(|flo| flo.flo_type() == type_name::()) - .map(|flo| BlobTag::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); - - tags.clone().for_each(|tag| { - log::info!("tag found {}", tag.0.values().iter().next().unwrap().1) - }); - - if tags.any(|flotag| { - flotag - .0 - .values() - .iter() - .next() - .is_some_and(|tagname| *tagname.1 == tag) - }) { - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_end", 1); - f.loop_node(crate::FledgerState::Forever).await?; - - return Ok(()); - } else { - log::info!("Tag not found..."); - } - // tags.storage - // .iter() - // .any(|flotag| flotag.1.values().get(&tag).is_some()); - // //.any(|flotag| flotag.1.values().iter().next().unwrap().1.eq(&tag)); - // //.any(|tag| tag.1.values() tag.1.cache().0.get_blob_mut().values.get(tag).is_some()); - // { - // log::info!("SIMULATION END"); - // absolute_counter!("fledger_simulation_end", 1); - // f.loop_node(crate::FledgerState::Forever).await?; - // - // return Ok(()); - // } - } - } - - async fn run_dht_create_page_with_fillers( - mut f: Fledger, - filler_amount: u32, - page_size: u32, - pages_propagation_delay: u32, - connection_delay: u32, - ) -> anyhow::Result<()> { - f.loop_node(crate::FledgerState::DHTAvailable).await?; - absolute_counter!("fledger_dht_connected", 1); - - log::info!("DHT CONNECTED"); - - log::info!("[Waiting for connections to settle]"); - log::info!("{} ms", connection_delay); - wait_ms(connection_delay as u64).await; - - //let router = f.node.dht_router.unwrap(); - let ds = f.node.dht_storage.as_mut().unwrap(); - let mut rv = RealmView::new_first(ds.clone()).await?; - - log::info!("[Create filler pages]"); - for i in 0..filler_amount { - let page_content = String::from_utf8(vec![b'-'; page_size as usize])?; - let flo_page = rv - .create_http( - &format!("simulation-filler-{}", i.to_string()), - page_content.clone(), - None, - flcrypto::access::Condition::Pass, - &[], - ) - .await - .unwrap(); - - log::info!( - "page {}/{}/{} | {} | {} ({}B -> {}B)", - flo_page.flo_id(), - flo_page.realm_id(), - flo_page.version(), - flo_page.values().iter().next().unwrap().1, - page_content.clone().chars().take(50).collect::(), - page_content.size(), - flo_page.size(), - ); - } - - log::info!("[Waiting for fillers to settle]"); - log::info!("{} ms", pages_propagation_delay); - - ds.broker.settle(Vec::new()).await?; - ds.sync()?; - wait_ms(pages_propagation_delay as u64).await; - - log::info!("[Sending simulation flo page]"); - let page_content = String::from_utf8(vec![b'o'; page_size as usize])?; - let page_links: HashMap> = HashMap::new(); - let page_path = "simulation-page"; - let page_id = U256::zero(); - - let flo_page = FloWrapper::from_type_config( - rv.realm.realm_id(), - flcrypto::access::Condition::Pass, - FloConfig { - cuckoo: flmodules::dht_storage::core::Cuckoo::None, - force_id: Some(page_id), - }, - BlobPage(Blob::make( - "re.fledg.page".into(), - page_links, - [("path".to_string(), page_path.into())].into(), - [("index.html".to_string(), page_content.into())].into(), - )), - &[], - )?; - // let flo_page = FloBlobPage::new_cuckoo( - // rv.realm.realm_id(), - // flcrypto::access::Condition::Pass, - // &format!("simulation-filler-{}", i.to_string()), - // Bytes::from(page_content), - // None, - // flmodules::dht_storage::core::Cuckoo::None.clone(), - // &[], - // )?; - // let flo_page = rv - // .create_http( - // "simulation-page", - // String::from_utf8(vec![b'o'; page_size as usize])?, - // None, - // flcrypto::access::Condition::Pass, - // &[], - // ) - // .await - // .unwrap(); - - let page_content = - String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()).unwrap(); - - log::info!( - "page {}/{}/{} | {} | {} ({}B -> {}B)", - flo_page.flo_id(), - flo_page.realm_id(), - flo_page.version(), - flo_page.values().iter().next().unwrap().1, - page_content.chars().take(50).collect::(), - page_content.size(), - flo_page.size(), - ); - - let signer = f.node.crypto_storage.get_signer(); - rv.set_realm_service("simulation-page", flo_page.blob_id(), &[&signer]) - .await?; - - ds.store_flo(flo_page.flo().clone())?; - wait_ms(1000).await; - - ds.sync()?; - - ds.broker.settle(Vec::new()).await?; - - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_success", 1); - - f.loop_node(crate::FledgerState::Forever).await?; - return Ok(()); - } - - async fn run_dht_wait_for_pages( - mut f: Fledger, - sampling_rate_ms: u32, - timeout_ms: u32, - amount: u32, - ) -> anyhow::Result<()> { - let start_instant = Instant::now(); - absolute_counter!("fledger_simulation_success", 0); - absolute_counter!("fledger_dht_connected", 0); - absolute_counter!("fledger_connected_total", 0); - - let timeout_result = timeout( - Duration::from_millis(timeout_ms.into()), - f.loop_node(crate::FledgerState::DHTAvailable), - ) - .await; - - if timeout_result.is_err() { - log::warn!("SIMULATION TIMEOUT WHILE CONNECTING TO DHT"); - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_timeout", 1); - } - - absolute_counter!("fledger_dht_connected", 1); - - log::info!("DHT CONNECTED"); - - //let ds = f.node.dht_storage.as_mut().unwrap(); - //let mut rv = RealmView::new_first(ds.clone()).await?; - - loop { - if start_instant.elapsed().as_millis() > timeout_ms as u128 { - log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_timeout", 1); - f.loop_node(crate::FledgerState::Forever).await?; - - return Ok(()); - } - - wait_ms(sampling_rate_ms.into()).await; - - increment_counter!("fledger_iterations_total"); - - absolute_counter!( - "fledger_connected_total", - f.node.dht_router.as_ref().unwrap().stats.borrow().active as u64 - ); - - //rv.update_all().await?; - //f.node.dht_storage.as_mut().unwrap().sync()?; - - let pages = f - .node - .dht_storage - .as_mut() - .unwrap() - .get_flos() - .await - .unwrap() - .clone(); - let pages = pages - .iter() - .filter(|flo| flo.flo_type() == type_name::()) - .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); - - let page_count = pages.count(); - absolute_counter!("fledger_pages_total", page_count as u64); - - if page_count >= amount as usize { - log::info!("enough pages received"); - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_success", 1); - f.loop_node(crate::FledgerState::Forever).await?; - return Ok(()); - } else { - log::info!("NOT enough pages received..."); - } - } - } - - async fn run_dht_fetch_simulation_page( - mut f: Fledger, - sampling_rate_ms: u32, - enable_sync: bool, - timeout_ms: u32, - experiment_id: u32, - ) -> anyhow::Result<()> { - let start_instant = Instant::now(); - absolute_counter!("fledger_simulation_success", 0); - absolute_counter!("fledger_connected_total", 0); - absolute_counter!("fledger_forwarded_flo_requests_total", 0); - absolute_counter!("fledger_forwarded_flo_meta_requests_total", 0); - absolute_counter!("fledger_blocked_flo_requests_total", 0); - absolute_counter!("fledger_flo_value_sent_total", 0); - absolute_counter!("fledger_flo_value_blocked_total", 0); - - let metrics = Metrics::new(experiment_id, f.node.node_config.info.name.clone()); - - let timeout_result = timeout( - Duration::from_millis(timeout_ms.into()), - f.loop_node(crate::FledgerState::DHTAvailable), - ) - .await; - - if timeout_result.is_err() { - log::warn!("SIMULATION TIMEOUT WHILE CONNECTING TO DHT"); - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_timeout", 1); - metrics.timeout(); - return Err(timeout_result.unwrap_err().into()); - } - - absolute_counter!("fledger_dht_connected", 1); - - log::info!("DHT CONNECTED"); - - let mut rv = RealmView::new_first(f.node.dht_storage.as_ref().unwrap().clone()).await?; - - let mut page_service: Option<&FloID>; - - let mut iteration = 0 as u32; - loop { - if start_instant.elapsed().as_millis() > timeout_ms as u128 { - log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_timeout", 1); - metrics.timeout(); - f.loop_node(crate::FledgerState::Forever).await?; - - return Ok(()); - } - - let ds = f.node.dht_storage.as_mut().unwrap(); - - wait_ms(sampling_rate_ms.into()).await; - iteration += 1; - - increment_counter!("fledger_iterations_total"); - - absolute_counter!( - "fledger_connected_total", - f.node.dht_router.as_ref().unwrap().stats.borrow().active as u64 - ); - - if enable_sync { - ds.sync()?; - } - // let _ = rv - // .update_all() - // .await - // .inspect_err(|e| log::error!("error when doing rv.update_all(): {e}")); - - let pages = ds - .get_flos() - .await - .unwrap_or_else(|e| { - log::error!("failed to get flos {e}"); - vec![] - }) - .clone(); - let pages = pages - .iter() - .filter(|flo| flo.flo_type() == type_name::()) - .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); - - let page_names = pages - .clone() - .map(|page| page.0.values().iter().next().unwrap().1.clone()); - - let simulation_page_stored_this_iteration = page_names - .clone() - .find(|name| name == "simulation-page") - .is_some(); - - let page_list = page_names - .clone() - .map(|name| { - return name.replace("simulation-filler-", ""); - }) - .collect::>(); - - let pages_csv = page_list.clone().join(", "); - log::info!("pages stored: {pages_csv}"); - - if iteration % 10 == 0 { - let ds_stats = ds.stats.borrow().experiment_stats.clone(); - metrics.update(pages_csv.clone(), ds_stats); - } - - if simulation_page_stored_this_iteration { - absolute_counter!("fledger_simulation_page_stored", 1); - } else { - absolute_counter!("fledger_simulation_page_stored", 0); - } - - absolute_counter!("fledger_pages_total", pages.count() as u64); - - let ds_size = ds.stats.borrow().realm_stats.iter().next().unwrap().1.size; - absolute_counter!("fledger_realm_storage_bytes", ds_size as u64); - - // let page_id_opt = Some(FloID::from_str( - // "c9e737fc7c55f404388d3eda20d5a047adc3b50e4ac59f25c0f9d8ce23d5fb94", - // )?); - let page_id_opt: Option = None; // Testing whether pages propagate with no - // get_flo - - let page_service_unmutable = rv.realm.cache().get_services().get("simulation-page"); - page_service = page_service_unmutable.clone(); - if page_service.is_none() { - if page_service.is_some() { - log::info!("service received"); - log::info!(" - id from service: {}", page_service.clone().unwrap()); - log::info!(" - id hardcoded : {}", page_id_opt.clone().unwrap()); - } - } - - if let Some(page_id) = page_id_opt { - let page_global_id = GlobalID::new(rv.realm.realm_id(), page_id.clone()); - let page_flo_wrapper_result: Result, Error> = - ds.get_flo(&page_global_id).await; - - if let Ok(page_flo_wrapper) = page_flo_wrapper_result { - let page_flo = page_flo_wrapper.flo(); - let page_blob = - BlobPage::from_rmp_bytes(page_flo.flo_type().as_str(), page_flo.data()) - .unwrap(); - let page_content = String::from_utf8( - page_blob - .0 - .datas() - .iter() - .next() - .unwrap() - .1 - .clone() - .to_vec(), - ) - .unwrap_or_default(); - log::info!( - "simulation page found with content: {}", - page_content.chars().take(50).collect::() - ); - - log::info!("SIMULATION END"); - absolute_counter!("fledger_simulation_success", 1); - metrics.update( - pages_csv.clone(), - ds.stats.borrow().experiment_stats.clone(), - ); - metrics.success(); - f.loop_node(crate::FledgerState::Forever).await?; - - return Ok(()); - } else { - increment_counter!("fledger_simulation_page_fetch_fail_total"); - - //log::info!("could not fetch page with id [{}].", page_id); - } - } else { - increment_counter!("fledger_simulation_service_fetch_fail_total"); - - log::info!("page_id not found in services..."); - } - } - } - - fn log_new_messages(f: &Fledger, acked_msg_ids: &mut Vec) { - let chat_events = f.node.gossip.as_ref().unwrap().chat_events(); - let chats: Vec<&Event> = chat_events - .iter() - .filter(|ev| !acked_msg_ids.contains(&ev.get_id())) - .collect(); - - if chats.len() <= 0 { - log::debug!("... No new message"); - } else { - log::info!("--- New Messages ---"); - for chat in chats { - acked_msg_ids.push(chat.get_id()); - log::info!(" [{}] {}", chat.src, chat.msg); - } } } } diff --git a/cli/fledger/src/simulation_chat/mod.rs b/cli/fledger/src/simulation_chat/mod.rs new file mode 100644 index 00000000..47a12cbb --- /dev/null +++ b/cli/fledger/src/simulation_chat/mod.rs @@ -0,0 +1 @@ +pub mod simulation; diff --git a/cli/fledger/src/simulation_chat/simulation.rs b/cli/fledger/src/simulation_chat/simulation.rs new file mode 100644 index 00000000..bca6337c --- /dev/null +++ b/cli/fledger/src/simulation_chat/simulation.rs @@ -0,0 +1,80 @@ +use crate::simulation::SimulationCommand; +use crate::Fledger; +use flarch::nodeids::U256; +use flarch::tasks::wait_ms; +use flmodules::gossip_events::core::Event; +use metrics::{absolute_counter, increment_counter}; + +#[derive(Clone)] +pub struct SimulationChat {} + +impl SimulationChat { + pub async fn run_chat( + mut f: Fledger, + simulation_args: SimulationCommand, + send_msg: Option, + recv_msg: Option, + ) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::Connected(1)).await?; + + if let Some(ref msg) = recv_msg { + log::info!("Waiting for simulation_chat message {}.", msg); + } + + if let Some(ref msg) = send_msg { + log::info!("Sending simulation_chat message {}.", msg); + f.node.add_chat_message(msg.into()).await?; + } + + let mut acked_msg_ids: Vec = Vec::new(); + + loop { + wait_ms(1000).await; + + let fledger_message_total = f.node.gossip.as_ref().unwrap().chat_events().len(); + let fledger_connected_total = f.node.nodes_connected()?.len(); + absolute_counter!( + "fledger_message_total", + fledger_message_total.try_into().unwrap() + ); + absolute_counter!("fledger_connected_total", fledger_connected_total as u64); + increment_counter!("fledger_iterations_total"); + + if simulation_args.print_new_messages { + Self::log_new_messages(&f, &mut acked_msg_ids); + } + + if let Some(ref msg) = recv_msg { + let gossip = f.node.gossip.as_ref(); + if gossip + .unwrap() + .chat_events() + .iter() + .any(|ev| ev.msg.eq(msg)) + { + log::info!("SIMULATION END"); + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } + } + } + } + + fn log_new_messages(f: &Fledger, acked_msg_ids: &mut Vec) { + let chat_events = f.node.gossip.as_ref().unwrap().chat_events(); + let chats: Vec<&Event> = chat_events + .iter() + .filter(|ev| !acked_msg_ids.contains(&ev.get_id())) + .collect(); + + if chats.len() <= 0 { + log::debug!("... No new message"); + } else { + log::info!("--- New Messages ---"); + for chat in chats { + acked_msg_ids.push(chat.get_id()); + log::info!(" [{}] {}", chat.src, chat.msg); + } + } + } +} diff --git a/cli/fledger/src/simulation_dht_target/mod.rs b/cli/fledger/src/simulation_dht_target/mod.rs new file mode 100644 index 00000000..512630f4 --- /dev/null +++ b/cli/fledger/src/simulation_dht_target/mod.rs @@ -0,0 +1,2 @@ +pub mod simulation; +pub mod stats; diff --git a/cli/fledger/src/simulation_dht_target/simulation.rs b/cli/fledger/src/simulation_dht_target/simulation.rs new file mode 100644 index 00000000..7edb357a --- /dev/null +++ b/cli/fledger/src/simulation_dht_target/simulation.rs @@ -0,0 +1,278 @@ +use anyhow::Error; +use flarch::{ + nodeids::U256, + tasks::{time::timeout, wait_ms}, +}; +use flcrypto::tofrombytes::ToFromBytes; +use flmodules::{ + dht_storage::{core::FloConfig, realm_view::RealmView}, + flo::{ + blob::{Blob, BlobAccess, BlobID, BlobPage}, + flo::{FloID, FloWrapper}, + realm::GlobalID, + }, +}; +use std::str::FromStr; +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; + +use crate::simulation_dht_target::stats::SimulationStats; +use crate::{metrics::Metrics, Fledger}; + +#[derive(Clone)] +pub struct SimulationDhtTarget {} + +impl SimulationDhtTarget { + pub async fn run_create_fillers_and_target( + mut f: Fledger, + filler_amount: u32, + page_size: u32, + pages_propagation_delay: u32, + connection_delay: u32, + experiment_id: u32, + ) -> anyhow::Result<()> { + let metrics = Metrics::new(experiment_id, f.node.node_config.info.name.clone()); + + f.loop_node(crate::FledgerState::DHTAvailable).await?; + log::info!("DHT CONNECTED"); + + log::info!("[Waiting for connections to settle]"); + log::info!("{} ms", connection_delay); + wait_ms(connection_delay as u64).await; + + //let router = f.node.dht_router.unwrap(); + let mut rv = RealmView::new_first(f.node.dht_storage.clone().unwrap()).await?; + + log::info!("[Create filler pages]"); + for i in 0..filler_amount { + let page_content = String::from_utf8(vec![b'-'; page_size as usize])?; + let flo_page = rv + .create_http( + &format!("simulation-filler-{}", i.to_string()), + page_content.clone(), + None, + flcrypto::access::Condition::Pass, + &[], + ) + .await + .unwrap(); + + log::info!( + "page {}/{}/{} | {} | {} ({}B -> {}B)", + flo_page.flo_id(), + flo_page.realm_id(), + flo_page.version(), + flo_page.values().iter().next().unwrap().1, + page_content.clone().chars().take(50).collect::(), + page_content.size(), + flo_page.size(), + ); + } + + log::info!("[Waiting for fillers to settle]"); + log::info!("{} ms", pages_propagation_delay); + + // ds.broker.settle, ds.sync + f.node + .dht_storage + .as_mut() + .unwrap() + .broker + .settle(Vec::new()) + .await?; + f.node.dht_storage.as_mut().unwrap().sync()?; + wait_ms(pages_propagation_delay as u64).await; + + log::info!("[Sending simulation flo page]"); + let page_content = String::from_utf8(vec![b'o'; page_size as usize])?; + let page_links: HashMap> = HashMap::new(); + let page_path = "simulation-page"; + let page_id = U256::zero(); + + let flo_page = FloWrapper::from_type_config( + rv.realm.realm_id(), + flcrypto::access::Condition::Pass, + FloConfig { + cuckoo: flmodules::dht_storage::core::Cuckoo::None, + force_id: Some(page_id), + }, + BlobPage(Blob::make( + "re.fledg.page".into(), + page_links, + [("path".to_string(), page_path.into())].into(), + [("index.html".to_string(), page_content.into())].into(), + )), + &[], + )?; + // let flo_page = FloBlobPage::new_cuckoo( + // rv.realm.realm_id(), + // flcrypto::access::Condition::Pass, + // &format!("simulation-filler-{}", i.to_string()), + // Bytes::from(page_content), + // None, + // flmodules::dht_storage::core::Cuckoo::None.clone(), + // &[], + // )?; + // let flo_page = rv + // .create_http( + // "simulation-page", + // String::from_utf8(vec![b'o'; page_size as usize])?, + // None, + // flcrypto::access::Condition::Pass, + // &[], + // ) + // .await + // .unwrap(); + + let page_content = + String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()).unwrap(); + + log::info!( + "page {}/{}/{} | {} | {} ({}B -> {}B)", + flo_page.flo_id(), + flo_page.realm_id(), + flo_page.version(), + flo_page.values().iter().next().unwrap().1, + page_content.chars().take(50).collect::(), + page_content.size(), + flo_page.size(), + ); + + let signer = f.node.crypto_storage.get_signer(); + rv.set_realm_service("simulation-page", flo_page.blob_id(), &[&signer]) + .await?; + + f.node + .dht_storage + .as_mut() + .unwrap() + .store_flo(flo_page.flo().clone())?; + wait_ms(1000).await; + + // ds.broker.settle, ds.sync + f.node + .dht_storage + .as_mut() + .unwrap() + .broker + .settle(Vec::new()) + .await?; + f.node.dht_storage.as_mut().unwrap().sync()?; + + log::info!("SIMULATION END"); + metrics.success(); + + f.loop_node(crate::FledgerState::Forever).await?; + Ok(()) + } + + pub async fn fetch_target( + mut f: Fledger, + loop_delay: u32, + enable_sync: bool, + timeout_ms: u32, + experiment_id: u32, + ) -> anyhow::Result<()> { + let start_instant = Instant::now(); + + let metrics = Metrics::new(experiment_id, f.node.node_config.info.name.clone()); + let mut simulation_metrics = SimulationStats::new(); + + let timeout_result = timeout( + Duration::from_millis(timeout_ms.into()), + f.loop_node(crate::FledgerState::DHTAvailable), + ) + .await; + + if timeout_result.is_err() { + log::warn!("SIMULATION TIMEOUT WHILE CONNECTING TO DHT"); + log::info!("SIMULATION END"); + metrics.timeout(); + return Err(timeout_result.unwrap_err().into()); + } + + log::info!("DHT CONNECTED"); + + let realm_id = RealmView::new_first(f.node.dht_storage.as_ref().unwrap().clone()) + .await? + .realm + .realm_id(); + + let mut iteration = 0u32; + loop { + if start_instant.elapsed().as_millis() > timeout_ms as u128 { + log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); + log::info!("SIMULATION END"); + metrics.timeout(); + f.loop_node(crate::FledgerState::Forever).await?; + + return Ok(()); + } + + wait_ms(loop_delay.into()).await; + iteration += 1; + + if enable_sync { + f.node.dht_storage.as_mut().unwrap().sync()?; + } + // let _ = rv + // .update_all() + // .await + // .inspect_err(|e| log::error!("error when doing rv.update_all(): {e}")); + + if iteration % 10 == 0 { + simulation_metrics.refresh(&mut f).await; + metrics.upload(simulation_metrics.clone()); + } + + let page_id = FloID::from_str( + "c9e737fc7c55f404388d3eda20d5a047adc3b50e4ac59f25c0f9d8ce23d5fb94", + )?; + + // todo!("test this"); + // continue; // Testing whether pages propagate with no + // get_flo + + let page_global_id = GlobalID::new(realm_id.clone(), page_id.clone()); + let page_flo_wrapper_result: Result, Error> = f + .node + .dht_storage + .as_mut() + .unwrap() + .get_flo(&page_global_id) + .await; + + if let Ok(page_flo_wrapper) = page_flo_wrapper_result { + let page_flo = page_flo_wrapper.flo(); + let page_blob = + BlobPage::from_rmp_bytes(page_flo.flo_type().as_str(), page_flo.data())?; + let page_content = String::from_utf8( + page_blob + .0 + .datas() + .iter() + .next() + .unwrap() + .1 + .clone() + .to_vec(), + ) + .unwrap_or_default(); + log::info!( + "simulation page found with content: {}", + page_content.chars().take(50).collect::() + ); + + log::info!("SIMULATION END"); + simulation_metrics.refresh(&mut f).await; + metrics.upload(simulation_metrics.clone()); + metrics.success(); + f.loop_node(crate::FledgerState::Forever).await?; + + return Ok(()); + } + } + } +} diff --git a/cli/fledger/src/simulation_dht_target/stats.rs b/cli/fledger/src/simulation_dht_target/stats.rs new file mode 100644 index 00000000..b60375a0 --- /dev/null +++ b/cli/fledger/src/simulation_dht_target/stats.rs @@ -0,0 +1,167 @@ +use crate::Fledger; +use flcrypto::tofrombytes::ToFromBytes; +use flmodules::dht_storage::broker::DHTStorage; +use flmodules::dht_storage::messages::ExperimentStats; +use flmodules::flo::blob::{BlobAccess, BlobPage}; +use metrics::absolute_counter; +use std::any::type_name; + +#[derive(Clone)] +pub struct SimulationStats { + pub pages: String, + pub target_page_stored_bool: u8, + pub connected_nodes_total: u32, + pub pages_stored_total: u32, + pub ds_size_bytes: u64, + + pub request_flos_received_total: u32, + pub ds_experiment_stats: ExperimentStats, +} + +impl SimulationStats { + pub fn new() -> Self { + Self { + pages: "".to_string(), + target_page_stored_bool: 0, + connected_nodes_total: 0, + pages_stored_total: 0, + ds_size_bytes: 0, + + request_flos_received_total: 0, + ds_experiment_stats: ExperimentStats::new(), + } + } + + pub async fn refresh_pages(&mut self, ds: &mut DHTStorage) { + let pages = ds + .get_flos() + .await + .unwrap_or_else(|e| { + log::error!("failed to get flos {e}"); + vec![] + }) + .clone(); + let pages = pages + .iter() + .filter(|flo| flo.flo_type() == type_name::()) + .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); + + let page_names = pages + .clone() + .map(|page| page.0.values().iter().next().unwrap().1.clone()); + + let simulation_page_stored_this_iteration = page_names + .clone() + .find(|name| name == "simulation-page") + .is_some(); + + let page_list = page_names + .clone() + .map(|name| { + return name.replace("simulation-filler-", ""); + }) + .collect::>(); + + let pages_csv = page_list.clone().join(", "); + + self.pages = pages_csv.clone(); + self.target_page_stored_bool = if simulation_page_stored_this_iteration { + 1 + } else { + 0 + }; + self.pages_stored_total = page_list.len() as u32; + + log::info!("pages stored: {pages_csv}"); + } + + pub async fn refresh(&mut self, f: &mut Fledger) { + let connected_nodes_total = f + .node + .dht_router + .clone() + .unwrap() + .stats + .borrow() + .active + .clone() as u32; + let ds = f.node.dht_storage.as_mut().unwrap(); + let ds_size = ds.stats.borrow().realm_stats.iter().next().unwrap().1.size; + self.ds_experiment_stats = ds.stats.borrow().experiment_stats.clone(); + self.connected_nodes_total = connected_nodes_total; + self.ds_size_bytes = ds_size; + + self.refresh_pages(ds).await; + + self.dump_influx(); + } + + pub fn dump_influx(&self) { + absolute_counter!("fledger_realm_storage_bytes", self.ds_size_bytes); + absolute_counter!("fledger_pages_total", self.pages_stored_total as u64); + absolute_counter!( + "fledger_simulation_page_stored", + self.target_page_stored_bool as u64 + ); + absolute_counter!("fledger_connected_total", self.connected_nodes_total as u64); + absolute_counter!( + "fledger_forwarded_flo_requests_total", + self.request_flos_received_total as u64 + ); + absolute_counter!( + "fledger_forwarded_flo_meta_requests_total", + self.ds_experiment_stats.available_flos_sent_total as u64 + ); + absolute_counter!( + "fledger_blocked_flo_meta_requests_total", + self.ds_experiment_stats.available_flos_sent_blocked_total as u64 + ); + absolute_counter!( + "fledger_flos_metas_received_from_neighbour", + self.ds_experiment_stats + .max_flo_metas_received_in_available_flos as u64 + ); + absolute_counter!( + "fledger_flos_requested_from_neighbour", + self.ds_experiment_stats + .max_flo_metas_requested_in_request_flos as u64 + ); + absolute_counter!( + "fledger_flos_ids_received_from_neighbour", + self.ds_experiment_stats + .max_flo_ids_received_in_request_flos as u64 + ); + absolute_counter!( + "fledger_forwarded_flo_requests_total", + self.ds_experiment_stats.flos_sent_total as u64 + ); + absolute_counter!( + "fledger_flos_sent_to_neighbour", + self.ds_experiment_stats.max_flos_sent_in_flos as u64 + ); + absolute_counter!( + "fledger_blocked_flo_requests_total", + self.ds_experiment_stats.flos_sent_blocked_total as u64 + ); + absolute_counter!( + "fledger_flo_value_sent_total", + self.ds_experiment_stats.flo_value_sent_total as u64 + ); + absolute_counter!( + "fledger_flo_value_blocked_total", + self.ds_experiment_stats.flo_value_sent_blocked_total as u64 + ); + absolute_counter!( + "fledger_blocked_flo_requests_total", + self.ds_experiment_stats.flos_sent_blocked_total as u64 + ); + absolute_counter!( + "fledger_flos_received_from_neighbour", // received + self.ds_experiment_stats.max_flos_received_in_flos as u64 + ); + absolute_counter!( + "fledger_ds_store_flo_total", + self.ds_experiment_stats.store_flo_total as u64 + ); + } +} diff --git a/cli/fledger/src/simulation_realm/mod.rs b/cli/fledger/src/simulation_realm/mod.rs new file mode 100644 index 00000000..47a12cbb --- /dev/null +++ b/cli/fledger/src/simulation_realm/mod.rs @@ -0,0 +1 @@ +pub mod simulation; diff --git a/cli/fledger/src/simulation_realm/simulation.rs b/cli/fledger/src/simulation_realm/simulation.rs new file mode 100644 index 00000000..158bee70 --- /dev/null +++ b/cli/fledger/src/simulation_realm/simulation.rs @@ -0,0 +1,17 @@ +use crate::Fledger; +use metrics::absolute_counter; + +#[derive(Clone)] +pub struct SimulationRealm {} + +impl SimulationRealm { + pub async fn run_dht_join_realm(mut f: Fledger) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::DHTAvailable).await?; + log::info!("SIMULATION END"); + + absolute_counter!("fledger_realms_total", 1); + + f.loop_node(crate::FledgerState::Forever).await?; + return Ok(()); + } +} diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 58333621..47e9b8e0 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -7,7 +7,6 @@ use flarch::{ platform_async_trait, }; use flcrypto::tofrombytes::ToFromBytes; -use metrics::{absolute_counter, increment_counter}; use serde::{Deserialize, Serialize}; use tokio::sync::watch; @@ -90,8 +89,49 @@ pub struct RealmStats { #[derive(Debug, Default, Clone)] pub struct ExperimentStats { - amount_flo_value_sent: u32, - amount_request_flo_metas_received: u32, + pub store_flo_total: u32, + pub flo_value_sent_total: u32, + pub flo_value_sent_blocked_total: u32, + pub available_flos_sent_total: u32, + pub available_flos_sent_blocked_total: u32, + pub request_flos_sent_total: u32, + pub flos_sent_total: u32, + pub flos_sent_blocked_total: u32, + pub max_flo_metas_received_in_available_flos: u32, + pub max_flo_metas_requested_in_request_flos: u32, + pub max_flo_ids_received_in_request_flos: u32, + pub max_flos_sent_in_flos: u32, + pub max_flos_received_in_flos: u32, +} + +/** + * It is necessary to call `self.refresh_stats()` after each change of a stat, + * and using methods would surely be worse than using macros, unless + * we would use a method for each stat. + * + * Since we want it to be as easy as possible to add new stats, we use macros. + */ +macro_rules! increment_stat { + ($self:ident, $stat:expr) => { + $stat += 1; + $self.refresh_stats(); + }; +} + +// macro_rules! absolute_stat { +// ($self:ident, $stat:expr, $value:expr) => { +// $stat = $value; +// $self.refresh_stats(); +// }; +// } + +macro_rules! max_stat { + ($self:ident, $stat:expr, $value:expr) => { + if $value > $stat { + $stat = $value; + $self.refresh_stats(); + } + }; } #[derive(Debug, Default, Clone)] @@ -116,7 +156,7 @@ pub struct Messages { pub static mut EVIL_NO_FORWARD: bool = false; impl Messages { - /// Returns a new chat module. + /// Returns a new simulation_chat module. pub fn new( ds: Box, config: DHTConfig, @@ -238,14 +278,13 @@ impl Messages { { // log::info!("sends flo {:?}", fc.0); if unsafe { !EVIL_NO_FORWARD } { - increment_counter!("fledger_flo_value_sent_total"); - self.incr_flo_value_sent(); + increment_stat!(self, self.experiment_stats.flo_value_sent_total); return MessageDest::FloValue(fc) .to_intern_out(origin) // .inspect(|msg| log::info!("{} sends {msg:?}", self.our_id)) .map_or(vec![], |msg| vec![msg]); } else { - increment_counter!("fledger_flo_value_blocked_total"); + increment_stat!(self, self.experiment_stats.flo_value_sent_blocked_total); return vec![]; } } @@ -328,8 +367,7 @@ impl Messages { } MessageNeighbour::RequestFloMetas(realm_id) => { if unsafe { !EVIL_NO_FORWARD } { - increment_counter!("fledger_forwarded_flo_meta_requests_total"); - self.incr_request_flo_metas_received(); + increment_stat!(self, self.experiment_stats.available_flos_sent_total); self.realms .get(&realm_id) .map(|realm| realm.get_flo_metas()) @@ -337,34 +375,42 @@ impl Messages { vec![MessageNeighbour::AvailableFlos(realm_id, fm)] }) } else { - increment_counter!("fledger_blocked_flo_meta_requests_total"); + increment_stat!( + self, + self.experiment_stats.available_flos_sent_blocked_total + ); vec![] } } MessageNeighbour::AvailableFlos(realm_id, flo_metas) => { - absolute_counter!( - "fledger_flos_metas_received_from_neighbour", - flo_metas.len() as u64 + max_stat!( + self, + self.experiment_stats + .max_flo_metas_received_in_available_flos, + flo_metas.len() as u32 ); + self.realms .get(&realm_id) .and_then(|realm| realm.sync_available(&flo_metas)) .map_or(vec![], |needed| { - //log::info!("Needed flos: {}", needed.len()); - absolute_counter!( - "fledger_flos_requested_from_neighbour", - needed.len() as u64 + max_stat!( + self, + self.experiment_stats + .max_flo_metas_requested_in_request_flos, + needed.len() as u32 ); vec![MessageNeighbour::RequestFlos(realm_id, needed)] }) } MessageNeighbour::RequestFlos(realm_id, flo_ids) => { - absolute_counter!( - "fledger_flos_ids_received_from_neighbour", // requested - flo_ids.len() as u64 + max_stat!( + self, + self.experiment_stats.max_flo_ids_received_in_request_flos, + flo_ids.len() as u32 ); if unsafe { !EVIL_NO_FORWARD } { - increment_counter!("fledger_forwarded_flo_requests_total"); + increment_stat!(self, self.experiment_stats.flos_sent_total); self.realms .get(&realm_id) .map(|realm| { @@ -386,18 +432,23 @@ impl Messages { }) .map_or(vec![], |flos| { //log::info!("Flos to send: {}", flos.len()); - absolute_counter!("fledger_flos_sent_to_neighbour", flos.len() as u64); + max_stat!( + self, + self.experiment_stats.max_flos_sent_in_flos, + flos.len() as u32 + ); vec![MessageNeighbour::Flos(flos)] }) } else { - increment_counter!("fledger_blocked_flo_requests_total"); + increment_stat!(self, self.experiment_stats.flos_sent_blocked_total); vec![] } } MessageNeighbour::Flos(flo_cuckoos) => { - absolute_counter!( - "fledger_flos_received_from_neighbour", // received - flo_cuckoos.len() as u64 + max_stat!( + self, + self.experiment_stats.max_flos_received_in_flos, + flo_cuckoos.len() as u32 ); for (flo, cuckoos) in flo_cuckoos { self.store_flo(flo.clone()); @@ -422,7 +473,7 @@ impl Messages { } fn store_flo(&mut self, flo: Flo) -> Vec { - increment_counter!("fledger_ds_store_flo_total"); + increment_stat!(self, self.experiment_stats.store_flo_total); let mut res = vec![]; if self.upsert_flo(flo.clone()) { // log::info!("{}: store_flo", self.our_id); @@ -504,16 +555,6 @@ impl Messages { .map(|s| (*self.ds).set(MODULE_NAME, &s)); } - fn incr_request_flo_metas_received(&mut self) { - self.experiment_stats.incr_request_flo_metas_received(); - self.refresh_stats(); - } - - fn incr_flo_value_sent(&mut self) { - self.experiment_stats.incr_flo_value_sent(); - self.refresh_stats(); - } - fn refresh_stats(&mut self) { self.tx.clone().map(|tx| { tx.send(Stats::from_realms( @@ -549,27 +590,8 @@ impl SubsystemHandler for Messages { } impl ExperimentStats { - fn new() -> Self { - Self { - amount_flo_value_sent: 0, - amount_request_flo_metas_received: 0, - } - } - - pub fn amount_flo_value_sent(&self) -> u32 { - self.amount_flo_value_sent - } - - pub fn amount_request_flo_metas_received(&self) -> u32 { - self.amount_request_flo_metas_received - } - - fn incr_flo_value_sent(&mut self) { - self.amount_flo_value_sent += 1; - } - - fn incr_request_flo_metas_received(&mut self) { - self.amount_request_flo_metas_received += 1; + pub fn new() -> Self { + Self::default() } } diff --git a/flmodules/src/dht_storage/realm_view.rs b/flmodules/src/dht_storage/realm_view.rs index be12d9f9..52699ed0 100644 --- a/flmodules/src/dht_storage/realm_view.rs +++ b/flmodules/src/dht_storage/realm_view.rs @@ -397,16 +397,16 @@ impl RealmView { self.dht_storage.convert(flo.cond(), &flo.realm_id()).await } - pub async fn update_realm(&mut self) -> anyhow::Result<&FloRealm>{ + pub async fn update_realm(&mut self) -> anyhow::Result<&FloRealm> { self.realm = self.dht_storage.get_flo(&self.realm.global_id()).await?; Ok(&self.realm) } - pub async fn update_all(&mut self) -> anyhow::Result<()>{ + pub async fn update_all(&mut self) -> anyhow::Result<()> { self.update_pages().await?; self.update_tags().await?; self.update_realm().await?; - + Ok(()) } } diff --git a/flmodules/src/flo/flo.rs b/flmodules/src/flo/flo.rs index 12283187..b194bf2a 100644 --- a/flmodules/src/flo/flo.rs +++ b/flmodules/src/flo/flo.rs @@ -59,7 +59,7 @@ pub struct Flo { // The data signature is verifiable by the latest cond. data_signature: ConditionSignature, // Because I thought that ed25519 signatures have a random nonce, when in - // fact the implementation of ed25519-dalek uses a nonce derived from the + // fact the implementation of ed25519-dalek uses a nonce derived from the // message, probably to avoid nonce-reuse... nonce: U256, // The first condition of this Flo, together with a signature on itself. diff --git a/flmodules/src/gossip_events/broker.rs b/flmodules/src/gossip_events/broker.rs index 6537de11..13ca23a3 100644 --- a/flmodules/src/gossip_events/broker.rs +++ b/flmodules/src/gossip_events/broker.rs @@ -80,7 +80,7 @@ impl Gossip { Ok(()) } - /// Gets a copy of all chat events stored in the module. + /// Gets a copy of all simulation_chat events stored in the module. pub fn chat_events(&self) -> Vec { self.storage.borrow().events(Category::TextMessage) } diff --git a/flmodules/src/gossip_events/messages.rs b/flmodules/src/gossip_events/messages.rs index b6b10252..2d088c3a 100644 --- a/flmodules/src/gossip_events/messages.rs +++ b/flmodules/src/gossip_events/messages.rs @@ -32,7 +32,7 @@ pub enum GossipOut { } /// The first module to use the random_connections is a copy of the previous -/// chat. +/// simulation_chat. /// Now it holds events of multiple categories and exchanges them between the /// nodes. pub struct Messages { @@ -57,7 +57,7 @@ impl std::fmt::Debug for Messages { } impl Messages { - /// Returns a new chat module. + /// Returns a new simulation_chat module. pub fn new( id: NodeID, storage: Box, diff --git a/flmodules/src/template/messages.rs b/flmodules/src/template/messages.rs index 15204491..0dc517c7 100644 --- a/flmodules/src/template/messages.rs +++ b/flmodules/src/template/messages.rs @@ -40,7 +40,7 @@ pub struct Messages { } impl Messages { - /// Returns a new chat module. + /// Returns a new simulation_chat module. pub fn new( storage: Box, cfg: TemplateConfig, diff --git a/flmodules/src/web_proxy/messages.rs b/flmodules/src/web_proxy/messages.rs index 936be393..f2b49733 100644 --- a/flmodules/src/web_proxy/messages.rs +++ b/flmodules/src/web_proxy/messages.rs @@ -52,7 +52,7 @@ pub struct Messages { } impl Messages { - /// Returns a new chat module. + /// Returns a new simulation_chat module. pub fn new( ds: Box, cfg: WebProxyConfig, diff --git a/flnode/src/node.rs b/flnode/src/node.rs index 68c9fd7a..7bc2f756 100644 --- a/flnode/src/node.rs +++ b/flnode/src/node.rs @@ -259,7 +259,7 @@ impl Node { } } - /// Adds a new chat message that will be broadcasted to the system. + /// Adds a new simulation_chat message that will be broadcasted to the system. pub async fn add_chat_message(&mut self, msg: String) -> anyhow::Result<()> { if let Some(g) = self.gossip.as_mut() { let event = core::Event { From 664ac42008b572fc8bebfdfdab91e19c86c526cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 9 Jun 2025 17:50:35 +0200 Subject: [PATCH 34/43] refactor: cleanup cli simulation launches --- Cargo.lock | 1 + cli/fledger/Cargo.toml | 1 + cli/fledger/src/main.rs | 96 +++++++++---------- cli/fledger/src/simulation.rs | 17 +++- .../src/simulation_dht_target/simulation.rs | 89 +++++++---------- 5 files changed, 92 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7722c60b..68096910 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1280,6 +1280,7 @@ name = "fledger" version = "0.9.2" dependencies = [ "anyhow", + "chrono", "clap", "clap-verbosity-flag", "env_logger", diff --git a/cli/fledger/Cargo.toml b/cli/fledger/Cargo.toml index ae8b8da5..8e9a6f6d 100644 --- a/cli/fledger/Cargo.toml +++ b/cli/fledger/Cargo.toml @@ -36,3 +36,4 @@ reqwest = { version = "0.12.19", features = [ "rustls-tls", "blocking", ], default-features = false } +chrono = "0.4.41" diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index b047ebd6..e9b16eea 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -142,62 +142,23 @@ async fn main() -> anyhow::Result<()> { logger.parse_env("RUST_LOG"); logger.try_init().expect("Failed to initialize logger"); - let storage = DataStorageFile::new(args.config.clone(), "fledger".into()); - let mut node_config = Node::get_config(storage.clone_box())?; - args.name - .as_ref() - .map(|name| node_config.info.name = name.clone()); - - // necessary to grab the variable for lifetime purposes. - let node_name = args.name.clone().unwrap_or("unknown".into()); - let _influx = Metrics::setup(node_name); - log::info!( "Starting app with version {}/{}", SIGNAL_VERSION, VERSION_STRING ); - // wait a random amount of time before running a simulation - // to avoid overloading the signaling server - if !args.bootwait_max != 0 { - match args.command.clone() { - Some(cmd) => match cmd { - Commands::Simulation(_) => { - if args.bootwait_max != 0 { - let randtime = random::() % args.bootwait_max; - log::info!("Waiting {}ms before running this node...", randtime); - wait_ms(randtime).await; - } - } - _ => {} - }, - _ => {} - } - } - unsafe { flmodules::dht_storage::messages::EVIL_NO_FORWARD = args.evil_noforward; flmodules::dht_router::messages::EVIL_NO_FORWARD = args.evil_noforward; } + absolute_counter!( "fledger_evil_noforward", if args.evil_noforward { 1 } else { 0 } ); - let cc = if args.disable_turn_stun { - ConnectionConfig::from_signal(&args.signal_url) - } else { - ConnectionConfig::new( - args.signal_url.clone().into(), - Some(HostLogin::from_url(&args.stun_url)), - Some(HostLogin::from_login_url(&args.turn_url)?), - ) - }; - log::debug!("Connecting to websocket at {:?}", cc); - let network = network_start(node_config.clone(), cc).await?; - let node = Node::start(Box::new(storage), node_config, network.broker).await?; - Fledger::run(node, args).await + Fledger::run(args).await } pub enum FledgerState { @@ -209,28 +170,63 @@ pub enum FledgerState { } impl Fledger { - async fn run(node: Node, args: Args) -> anyhow::Result<()> { + async fn make_node(args: Args) -> anyhow::Result { + let storage = DataStorageFile::new(args.config.clone(), "fledger".into()); + let mut node_config = Node::get_config(storage.clone_box())?; + args.name + .as_ref() + .map(|name| node_config.info.name = name.clone()); + + // necessary to grab the variable for lifetime purposes. + let node_name = args.name.clone().unwrap_or("unknown".into()); + let _influx = Metrics::setup(node_name); + + let cc = if args.disable_turn_stun { + ConnectionConfig::from_signal(&args.signal_url) + } else { + ConnectionConfig::new( + args.signal_url.clone().into(), + Some(HostLogin::from_url(&args.stun_url)), + Some(HostLogin::from_login_url(&args.turn_url)?), + ) + }; + log::debug!("Connecting to websocket at {:?}", cc); + let network = network_start(node_config.clone(), cc).await?; + let node = Node::start(Box::new(storage), node_config, network.broker).await?; + Ok(node) + } + + async fn make_fledger(args: Args) -> anyhow::Result { + let node = Self::make_node(args.clone()).await?; let nc = &node.node_config.info; log::info!("Started node {}: {}", nc.get_id(), nc.name); - let mut f = Fledger { + Ok(Fledger { ds: node.dht_storage.as_ref().unwrap().clone(), dr: node.dht_router.as_ref().unwrap().clone(), node, - args: args.clone(), - }; + args, + }) + } - match f.args.command.clone() { + async fn run(args: Args) -> anyhow::Result<()> { + match args.command.clone() { Some(cmd) => match cmd { - Commands::Realm { command } => RealmHandler::run(f, command).await, + Commands::Realm { command } => { + RealmHandler::run(Self::make_fledger(args)?, command).await + } Commands::Crypto {} => todo!(), Commands::Stats {} => todo!(), - Commands::Page { command } => Page::run(f, command).await, + Commands::Page { command } => Page::run(Self::make_fledger(args)?, command).await, Commands::Simulation(command) => { - SimulationHandler::run(f, command, args.loop_delay).await + SimulationHandler::run(Self::make_fledger(args)?, command).await } }, - None => f.loop_node(FledgerState::Forever).await, + None => { + Self::make_fledger(args) + .loop_node(FledgerState::Forever) + .await + } } } diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index d7e69b8d..17b5aa03 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -2,6 +2,8 @@ use crate::simulation_chat::simulation::SimulationChat; use crate::simulation_realm::simulation::SimulationRealm; use crate::{simulation_dht_target::simulation::SimulationDhtTarget, Fledger}; use clap::{arg, Args, Subcommand}; +use flarch::random; +use flarch::tasks::wait_ms; #[derive(Args, Debug, Clone)] pub struct SimulationCommand { @@ -60,11 +62,16 @@ pub enum SimulationSubcommand { pub struct SimulationHandler {} impl SimulationHandler { - pub async fn run( - f: Fledger, - command: SimulationCommand, - loop_delay: u32, - ) -> anyhow::Result<()> { + pub async fn run(f: Fledger, command: SimulationCommand) -> anyhow::Result<()> { + // wait a random amount of time before running a simulation + // to avoid overloading the signaling server + if f.args.bootwait_max != 0 { + let randtime = random::() % args.bootwait_max; + log::info!("Waiting {}ms before running this node...", randtime); + wait_ms(randtime).await; + } + + let loop_delay = f.args.loop_delay; match command.subcommand.clone() { SimulationSubcommand::Chat { send_msg, recv_msg } => { SimulationChat::run_chat(f, command, send_msg, recv_msg).await diff --git a/cli/fledger/src/simulation_dht_target/simulation.rs b/cli/fledger/src/simulation_dht_target/simulation.rs index 7edb357a..4c45b4ac 100644 --- a/cli/fledger/src/simulation_dht_target/simulation.rs +++ b/cli/fledger/src/simulation_dht_target/simulation.rs @@ -1,22 +1,16 @@ use anyhow::Error; -use flarch::{ - nodeids::U256, - tasks::{time::timeout, wait_ms}, -}; +use flarch::tasks::{time::timeout, wait_ms}; use flcrypto::tofrombytes::ToFromBytes; use flmodules::{ - dht_storage::{core::FloConfig, realm_view::RealmView}, + dht_storage::realm_view::RealmView, flo::{ - blob::{Blob, BlobAccess, BlobID, BlobPage}, + blob::{BlobAccess, BlobPage}, flo::{FloID, FloWrapper}, realm::GlobalID, }, }; use std::str::FromStr; -use std::{ - collections::HashMap, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use crate::simulation_dht_target::stats::SimulationStats; use crate::{metrics::Metrics, Fledger}; @@ -86,45 +80,15 @@ impl SimulationDhtTarget { wait_ms(pages_propagation_delay as u64).await; log::info!("[Sending simulation flo page]"); - let page_content = String::from_utf8(vec![b'o'; page_size as usize])?; - let page_links: HashMap> = HashMap::new(); - let page_path = "simulation-page"; - let page_id = U256::zero(); - - let flo_page = FloWrapper::from_type_config( - rv.realm.realm_id(), - flcrypto::access::Condition::Pass, - FloConfig { - cuckoo: flmodules::dht_storage::core::Cuckoo::None, - force_id: Some(page_id), - }, - BlobPage(Blob::make( - "re.fledg.page".into(), - page_links, - [("path".to_string(), page_path.into())].into(), - [("index.html".to_string(), page_content.into())].into(), - )), - &[], - )?; - // let flo_page = FloBlobPage::new_cuckoo( - // rv.realm.realm_id(), - // flcrypto::access::Condition::Pass, - // &format!("simulation-filler-{}", i.to_string()), - // Bytes::from(page_content), - // None, - // flmodules::dht_storage::core::Cuckoo::None.clone(), - // &[], - // )?; - // let flo_page = rv - // .create_http( - // "simulation-page", - // String::from_utf8(vec![b'o'; page_size as usize])?, - // None, - // flcrypto::access::Condition::Pass, - // &[], - // ) - // .await - // .unwrap(); + let flo_page = rv + .create_http( + "simulation-page", + String::from_utf8(vec![b'o'; page_size as usize])?, + None, + flcrypto::access::Condition::Pass, + &[], + ) + .await?; let page_content = String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()).unwrap(); @@ -140,15 +104,14 @@ impl SimulationDhtTarget { flo_page.size(), ); - let signer = f.node.crypto_storage.get_signer(); - rv.set_realm_service("simulation-page", flo_page.blob_id(), &[&signer]) - .await?; - f.node .dht_storage .as_mut() .unwrap() .store_flo(flo_page.flo().clone())?; + + metrics.upload_target_page_id(flo_page.flo_id().to_string()); + wait_ms(1000).await; // ds.broker.settle, ds.sync @@ -201,6 +164,10 @@ impl SimulationDhtTarget { .realm_id(); let mut iteration = 0u32; + let mut page_id_opt: Option = None; + + // Loop until page_id found + loop { if start_instant.elapsed().as_millis() > timeout_ms as u128 { log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); @@ -227,14 +194,22 @@ impl SimulationDhtTarget { metrics.upload(simulation_metrics.clone()); } - let page_id = FloID::from_str( - "c9e737fc7c55f404388d3eda20d5a047adc3b50e4ac59f25c0f9d8ce23d5fb94", - )?; + // Stop here each iteration + // until page id is known + if page_id_opt.is_none() { + page_id_opt = metrics.pull_page_id(); + if page_id_opt.is_none() { + log::info!("failed to get page id"); + continue; + } + } // todo!("test this"); - // continue; // Testing whether pages propagate with no + //continue; // Testing whether pages propagate with no // get_flo + let page_id = FloID::from_str(&page_id_opt.clone().unwrap())?; + let page_global_id = GlobalID::new(realm_id.clone(), page_id.clone()); let page_flo_wrapper_result: Result, Error> = f .node From 5a263cf8335acb75be84bf0ac04699157396cf2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 9 Jun 2025 17:51:11 +0200 Subject: [PATCH 35/43] feat: live push to influxdb --- cli/fledger/src/metrics.rs | 242 +++++++++++++++++++++++++++++++------ 1 file changed, 206 insertions(+), 36 deletions(-) diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs index 1e0ddc16..d82ad754 100644 --- a/cli/fledger/src/metrics.rs +++ b/cli/fledger/src/metrics.rs @@ -1,16 +1,18 @@ use std::{collections::HashMap, fs::File, time::Duration}; +use crate::simulation_dht_target::stats::SimulationStats; use anyhow::Error; use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; use serde_json::Value; - -use crate::simulation_dht_target::stats::SimulationStats; +use tokio::time::Instant; #[derive(Clone)] pub struct Metrics { experiment_id: u32, node_name: String, node_id: Option, + + client: reqwest::blocking::Client, } impl Metrics { @@ -19,6 +21,7 @@ impl Metrics { experiment_id, node_name, node_id: None, + client: reqwest::blocking::Client::new(), }; let _ = metrics.api_create_node(); @@ -37,94 +40,145 @@ impl Metrics { .expect("could not setup influx recorder"); } - pub fn upload(&self, simulation_metrics: SimulationStats) { + pub fn upload(&self, stats: SimulationStats) { let mut data = HashMap::new(); - data.insert("pages", simulation_metrics.pages.clone()); - self.api_put_data(data); + data.insert("pages", stats.pages.clone()); + self.api_put_data_node(data); let mut datapoints = HashMap::new(); datapoints.insert( "connected_nodes_total", - simulation_metrics.connected_nodes_total.to_string(), + stats.connected_nodes_total.to_string(), ); self.api_post_datapoints(datapoints); let mut timeless_datapoints = HashMap::new(); timeless_datapoints.insert( "target_page_stored_bool", - simulation_metrics.target_page_stored_bool.to_string(), + stats.target_page_stored_bool.to_string(), ); timeless_datapoints.insert( "connected_nodes_total", - simulation_metrics.connected_nodes_total.to_string(), - ); - timeless_datapoints.insert( - "pages_stored_total", - simulation_metrics.pages_stored_total.to_string(), - ); - timeless_datapoints.insert( - "ds_size_bytes", - simulation_metrics.ds_size_bytes.to_string(), + stats.connected_nodes_total.to_string(), ); + timeless_datapoints.insert("pages_stored_total", stats.pages_stored_total.to_string()); + timeless_datapoints.insert("ds_size_bytes", stats.ds_size_bytes.to_string()); timeless_datapoints.insert( "flos_sent_total", - simulation_metrics - .ds_experiment_stats - .max_flos_sent_in_flos - .to_string(), + stats.ds_experiment_stats.max_flos_sent_in_flos.to_string(), ); timeless_datapoints.insert( "available_flos_sent_total", - simulation_metrics - .ds_experiment_stats - .flo_value_sent_total - .to_string(), + stats.ds_experiment_stats.flo_value_sent_total.to_string(), ); timeless_datapoints.insert( "available_flos_sent_blocked_total", - simulation_metrics + stats .ds_experiment_stats .flo_value_sent_blocked_total .to_string(), ); timeless_datapoints.insert( "max_flo_metas_received_in_available_flos", - simulation_metrics + stats .ds_experiment_stats .max_flo_metas_received_in_available_flos .to_string(), ); timeless_datapoints.insert( "max_flo_metas_requested_in_request_flos", - simulation_metrics + stats .ds_experiment_stats .max_flo_metas_requested_in_request_flos .to_string(), ); self.api_post_timeless_datapoints(timeless_datapoints); + + self.influx_push(stats) + } + + pub fn upload_target_page_id(&self, target_page_id: String) { + let mut data = HashMap::new(); + data.insert("target_page_id", target_page_id); + self.api_put_data_experiment(data); + } + + pub fn pull_page_id(&self) -> Option { + match self + .client + .get(format!( + "https://fledger.yohan.ch/api/experiments/{}/target-page-id", + self.experiment_id + )) + .header("Accept", "application/json") + .header( + "Authorization", + "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", + ) + .send() + { + Ok(resp) => { + if resp.status().is_success() { + let text = resp.text().unwrap(); + log::info!("Successfully pulled page id: {}", text); + let response_data: Value = serde_json::from_str(&text).unwrap(); + Some( + response_data["target_page_id"] + .as_str() + .unwrap() + .to_string(), + ) + } else { + log::error!("Failed to pull page id: {}", resp.status()); + None + } + } + Err(err) => { + log::error!("Error pulling page id: {}", err); + None + } + } } pub fn timeout(&self) { let mut data = HashMap::new(); data.insert("status", "timeout".to_string()); //absolute_counter!("fledger_simulation_timeout", 1); - self.api_put_data(data); + self.api_put_data_node(data); } pub fn success(&self) { let mut data = HashMap::new(); data.insert("status", "success".to_string()); //absolute_counter!("fledger_simulation_success", 1); - self.api_put_data(data); + self.api_put_data_node(data); } - pub fn api_put_data(&self, data: HashMap<&str, String>) { - match reqwest::blocking::Client::new() - .put(format!( + pub fn api_put_data_node(&self, data: HashMap<&str, String>) { + self.api_put_data( + format!( "https://fledger.yohan.ch/api/nodes/{}", self.node_id.clone().unwrap() - )) + ), + data, + ); + } + + pub fn api_put_data_experiment(&self, data: HashMap<&str, String>) { + self.api_put_data( + format!( + "https://fledger.yohan.ch/api/experiments/{}", + self.experiment_id + ), + data, + ); + } + + pub fn api_put_data(&self, url: String, data: HashMap<&str, String>) { + let start = Instant::now(); + match reqwest::blocking::Client::new() + .put(url) .json(&data) .header("Accept", "application/json") .header( @@ -133,7 +187,11 @@ impl Metrics { ) .send() { - Ok(resp) => log::info!("Successful API request: {}", resp.text().unwrap()), + Ok(resp) => log::info!( + "Successful API request in {}ms: {}", + start.elapsed().as_millis(), + resp.text().unwrap() + ), Err(err) => log::error!("Error: {}", err), }; } @@ -147,6 +205,7 @@ impl Metrics { } pub fn api_post_nodes(&self, resource: String, data: HashMap<&str, String>) { + let start = Instant::now(); match reqwest::blocking::Client::new() .post(format!( "https://fledger.yohan.ch/api/nodes/{}/{resource}", @@ -161,7 +220,8 @@ impl Metrics { .send() { Ok(resp) => log::info!( - "Successful API request ({resource}): {}", + "Successful API request in {}ms ({resource}): {}", + start.elapsed().as_millis(), resp.text().unwrap() ), Err(err) => log::error!("Error: {}", err), @@ -172,7 +232,8 @@ impl Metrics { let mut data = HashMap::new(); data.insert("name", self.node_name.clone()); - match reqwest::blocking::Client::new() + match self + .client .post(format!( "https://fledger.yohan.ch/api/experiments/{}/nodes/", self.experiment_id @@ -198,4 +259,113 @@ impl Metrics { } }; } + + fn create_influx_line(&self, measurement: String, value: u32) -> String { + let node_name = self.node_name.clone(); + format!("{measurement},node_name={node_name} value={value}") + } + + fn influx_push(&self, stats: SimulationStats) { + let mut lines = Vec::new(); + lines.push(self.create_influx_line( + "fledger_realm_storage_bytes".to_string(), + stats.ds_size_bytes as u32, + )); + lines.push( + self.create_influx_line("fledger_pages_total".to_string(), stats.pages_stored_total), + ); + lines.push(self.create_influx_line( + "fledger_simulation_page_stored".to_string(), + stats.target_page_stored_bool as u32, + )); + lines.push(self.create_influx_line( + "fledger_connected_total".to_string(), + stats.connected_nodes_total, + )); + lines.push(self.create_influx_line( + "fledger_forwarded_flo_requests_total".to_string(), + stats.request_flos_received_total, + )); + lines.push(self.create_influx_line( + "fledger_forwarded_flo_meta_requests_total".to_string(), + stats.ds_experiment_stats.available_flos_sent_total, + )); + lines.push(self.create_influx_line( + "fledger_blocked_flo_meta_requests_total".to_string(), + stats.ds_experiment_stats.available_flos_sent_blocked_total, + )); + lines.push( + self.create_influx_line( + "fledger_flos_metas_received_from_neighbour".to_string(), + stats + .ds_experiment_stats + .max_flo_metas_received_in_available_flos, + ), + ); + lines.push( + self.create_influx_line( + "fledger_flos_requested_from_neighbour".to_string(), + stats + .ds_experiment_stats + .max_flo_metas_requested_in_request_flos, + ), + ); + lines.push( + self.create_influx_line( + "fledger_flos_ids_received_from_neighbour".to_string(), + stats + .ds_experiment_stats + .max_flo_ids_received_in_request_flos, + ), + ); + lines.push(self.create_influx_line( + "fledger_forwarded_flo_requests_total".to_string(), + stats.ds_experiment_stats.flos_sent_total, + )); + lines.push(self.create_influx_line( + "fledger_flos_sent_to_neighbour".to_string(), + stats.ds_experiment_stats.max_flos_sent_in_flos, + )); + lines.push(self.create_influx_line( + "fledger_blocked_flo_requests_total".to_string(), + stats.ds_experiment_stats.flos_sent_blocked_total, + )); + lines.push(self.create_influx_line( + "fledger_flo_value_sent_total".to_string(), + stats.ds_experiment_stats.flo_value_sent_total, + )); + lines.push(self.create_influx_line( + "fledger_flo_value_blocked_total".to_string(), + stats.ds_experiment_stats.flo_value_sent_blocked_total, + )); + lines.push(self.create_influx_line( + "fledger_blocked_flo_requests_total".to_string(), + stats.ds_experiment_stats.flos_sent_blocked_total, + )); + lines.push(self.create_influx_line( + "fledger_flos_received_from_neighbour".to_string(), // received + stats.ds_experiment_stats.max_flos_received_in_flos, + )); + lines.push(self.create_influx_line( + "fledger_ds_store_flo_total".to_string(), + stats.ds_experiment_stats.store_flo_total, + )); + + let influx_data = lines.join("\n"); + + let start = Instant::now(); + match self. + client + .post("https://influxdb.abehssera.com/api/v2/write?org=fledger&bucket=fledger&precision=ns") + .body(influx_data) + .header( + "Authorization", + "Token F7y_RJHnXA0szQHDhEiuRDAw7B2etGywSc-wdMK-BJtkXwplqXe5ogCcXDEJJR18ZvWJ87kwxckl6n1lFu9B-Q==", + ) + .send() + { + Ok(resp) => log::info!("Successful write to influxdb ({}ms): {}", start.elapsed().as_millis(),resp.text().unwrap()), + Err(err) => log::error!("Error when writing to influxdb: {}", err), + }; + } } From 340aabb6de34c2c12a0f30a85287420241ea8855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Mon, 9 Jun 2025 18:10:56 +0200 Subject: [PATCH 36/43] fix: does not compile --- cli/fledger/src/api/influx.rs | 42 +++++++++++++++++++++++++++++++++++ cli/fledger/src/api/mod.rs | 1 + cli/fledger/src/main.rs | 9 +++++--- cli/fledger/src/metrics.rs | 26 ++++++++-------------- cli/fledger/src/simulation.rs | 2 +- 5 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 cli/fledger/src/api/influx.rs create mode 100644 cli/fledger/src/api/mod.rs diff --git a/cli/fledger/src/api/influx.rs b/cli/fledger/src/api/influx.rs new file mode 100644 index 00000000..4acc45d2 --- /dev/null +++ b/cli/fledger/src/api/influx.rs @@ -0,0 +1,42 @@ +use tokio::time::Instant; + +#[derive(Clone)] +pub struct InfluxApi { + client: reqwest::blocking::Client, +} + +impl InfluxApi { + pub fn new() -> Self { + InfluxApi { + client: reqwest::blocking::Client::new(), + } + } + + pub fn write(&self, influx_data: String) -> Result { + let start = Instant::now(); + match self. + client + .post("https://influxdb.abehssera.com/api/v2/write?org=fledger&bucket=fledger&precision=ns") + .body(influx_data) + .header( + "Authorization", + "Token F7y_RJHnXA0szQHDhEiuRDAw7B2etGywSc-wdMK-BJtkXwplqXe5ogCcXDEJJR18ZvWJ87kwxckl6n1lFu9B-Q==", + ) + .send() + { + Ok(resp) => { + let text = resp.text()?; + log::info!( + "Successful write to influxdb ({}ms): {}", + start.elapsed().as_millis(), + text.clone(), + ); + Ok(text) + }, + Err(err) => { + log::error!("Error when writing to influxdb: {}", err); + Err(err.into()) + }, + } + } +} diff --git a/cli/fledger/src/api/mod.rs b/cli/fledger/src/api/mod.rs new file mode 100644 index 00000000..ec390ec7 --- /dev/null +++ b/cli/fledger/src/api/mod.rs @@ -0,0 +1 @@ +pub mod influx; diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index e9b16eea..3956b135 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -18,6 +18,7 @@ use page::{Page, PageCommands}; use realm::{RealmCommands, RealmHandler}; use simulation::{SimulationCommand, SimulationHandler}; +mod api; mod metrics; mod page; mod realm; @@ -213,17 +214,19 @@ impl Fledger { match args.command.clone() { Some(cmd) => match cmd { Commands::Realm { command } => { - RealmHandler::run(Self::make_fledger(args)?, command).await + RealmHandler::run(Self::make_fledger(args).await?, command).await } Commands::Crypto {} => todo!(), Commands::Stats {} => todo!(), - Commands::Page { command } => Page::run(Self::make_fledger(args)?, command).await, + Commands::Page { command } => { + Page::run(Self::make_fledger(args).await?, command).await Commands::Simulation(command) => { - SimulationHandler::run(Self::make_fledger(args)?, command).await + SimulationHandler::run(Self::make_fledger(args).await?, command).await } }, None => { Self::make_fledger(args) + .await? .loop_node(FledgerState::Forever) .await } diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs index d82ad754..9413cdd8 100644 --- a/cli/fledger/src/metrics.rs +++ b/cli/fledger/src/metrics.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, fs::File, time::Duration}; +use crate::api::influx::InfluxApi; use crate::simulation_dht_target::stats::SimulationStats; use anyhow::Error; use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; @@ -13,6 +14,7 @@ pub struct Metrics { node_id: Option, client: reqwest::blocking::Client, + influx_api: InfluxApi, } impl Metrics { @@ -22,6 +24,7 @@ impl Metrics { node_name, node_id: None, client: reqwest::blocking::Client::new(), + influx_api: InfluxApi::new(), }; let _ = metrics.api_create_node(); @@ -95,7 +98,7 @@ impl Metrics { ); self.api_post_timeless_datapoints(timeless_datapoints); - self.influx_push(stats) + self.influx_write(stats) } pub fn upload_target_page_id(&self, target_page_id: String) { @@ -265,7 +268,7 @@ impl Metrics { format!("{measurement},node_name={node_name} value={value}") } - fn influx_push(&self, stats: SimulationStats) { + fn make_influx_data(&self, stats: SimulationStats) -> String { let mut lines = Vec::new(); lines.push(self.create_influx_line( "fledger_realm_storage_bytes".to_string(), @@ -351,21 +354,10 @@ impl Metrics { stats.ds_experiment_stats.store_flo_total, )); - let influx_data = lines.join("\n"); + lines.join("\n") + } - let start = Instant::now(); - match self. - client - .post("https://influxdb.abehssera.com/api/v2/write?org=fledger&bucket=fledger&precision=ns") - .body(influx_data) - .header( - "Authorization", - "Token F7y_RJHnXA0szQHDhEiuRDAw7B2etGywSc-wdMK-BJtkXwplqXe5ogCcXDEJJR18ZvWJ87kwxckl6n1lFu9B-Q==", - ) - .send() - { - Ok(resp) => log::info!("Successful write to influxdb ({}ms): {}", start.elapsed().as_millis(),resp.text().unwrap()), - Err(err) => log::error!("Error when writing to influxdb: {}", err), - }; + fn influx_write(&self, stats: SimulationStats) -> () { + let _ = self.influx_api.write(self.make_influx_data(stats)); } } diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 17b5aa03..1f246e60 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -66,7 +66,7 @@ impl SimulationHandler { // wait a random amount of time before running a simulation // to avoid overloading the signaling server if f.args.bootwait_max != 0 { - let randtime = random::() % args.bootwait_max; + let randtime = random::() % f.args.bootwait_max; log::info!("Waiting {}ms before running this node...", randtime); wait_ms(randtime).await; } From d60637bcd96e297f5b29558170901eeac781fa9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Tue, 10 Jun 2025 11:53:33 +0200 Subject: [PATCH 37/43] feat: hermes --- Cargo.lock | 7 +- cli/fledger/Cargo.toml | 14 +- cli/fledger/src/api/mod.rs | 1 - cli/fledger/src/hermes/api.rs | 81 ++++ cli/fledger/src/hermes/mod.rs | 3 + cli/fledger/src/hermes/snapshot.rs | 98 +++++ cli/fledger/src/hermes/update_response.rs | 10 + .../src/{api/influx.rs => influx/api.rs} | 8 +- cli/fledger/src/influx/lines.rs | 95 +++++ cli/fledger/src/influx/mod.rs | 2 + cli/fledger/src/main.rs | 28 +- cli/fledger/src/metrics.rs | 363 ------------------ cli/fledger/src/simulation_chat/simulation.rs | 10 - cli/fledger/src/simulation_dht_target/mod.rs | 1 - .../src/simulation_dht_target/simulation.rs | 37 +- .../src/simulation_dht_target/stats.rs | 167 -------- .../src/simulation_realm/simulation.rs | 5 +- cli/fledger/src/state.rs | 111 ++++++ flmodules/src/dht_storage/messages.rs | 14 +- 19 files changed, 436 insertions(+), 619 deletions(-) delete mode 100644 cli/fledger/src/api/mod.rs create mode 100644 cli/fledger/src/hermes/api.rs create mode 100644 cli/fledger/src/hermes/mod.rs create mode 100644 cli/fledger/src/hermes/snapshot.rs create mode 100644 cli/fledger/src/hermes/update_response.rs rename cli/fledger/src/{api/influx.rs => influx/api.rs} (88%) create mode 100644 cli/fledger/src/influx/lines.rs create mode 100644 cli/fledger/src/influx/mod.rs delete mode 100644 cli/fledger/src/metrics.rs delete mode 100644 cli/fledger/src/simulation_dht_target/stats.rs create mode 100644 cli/fledger/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 68096910..682e4acb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1280,7 +1280,6 @@ name = "fledger" version = "0.9.2" dependencies = [ "anyhow", - "chrono", "clap", "clap-verbosity-flag", "env_logger", @@ -1289,14 +1288,10 @@ dependencies = [ "flmodules", "flnode", "log", - "metrics", - "metrics-exporter-influx", - "rand 0.9.1", "reqwest", + "serde", "serde_json", - "thiserror 2.0.12", "tokio", - "webrtc-util", ] [[package]] diff --git a/cli/fledger/Cargo.toml b/cli/fledger/Cargo.toml index 8e9a6f6d..d3d04765 100644 --- a/cli/fledger/Cargo.toml +++ b/cli/fledger/Cargo.toml @@ -22,18 +22,12 @@ clap = "4" clap-verbosity-flag = "3" env_logger = "0.11" log = "0.4" -thiserror = "2" tokio = "1" -webrtc-util = "0.10" -metrics = "0.21.1" -metrics-exporter-influx = { version = "0.2.2", path = "../../metrics-exporter-influx" } -rand = "0.9.1" serde_json = "1.0.140" -#openssl = { version = "0.10.73", features = ["vendored"] } reqwest = { version = "0.12.19", features = [ - "json", - "rustls-tls", - "blocking", + "json", + "rustls-tls", + "blocking", ], default-features = false } -chrono = "0.4.41" +serde = { version = "1.0.219", features = ["derive"] } diff --git a/cli/fledger/src/api/mod.rs b/cli/fledger/src/api/mod.rs deleted file mode 100644 index ec390ec7..00000000 --- a/cli/fledger/src/api/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod influx; diff --git a/cli/fledger/src/hermes/api.rs b/cli/fledger/src/hermes/api.rs new file mode 100644 index 00000000..33487f4b --- /dev/null +++ b/cli/fledger/src/hermes/api.rs @@ -0,0 +1,81 @@ +use crate::hermes::snapshot::Snapshot; +use crate::hermes::update_response::UpdateResponse; +use crate::state::SimulationState; +use anyhow::Error; +use reqwest::Method; +use serde::Serialize; +use serde_json::Value; +use std::collections::HashMap; +use tokio::time::Instant; + +// Command and Control server for the Fledger CLI +// +// Hermes is both a command and control server and a metrics collector for the Fledger CLI. +// It has a dashboard that allows users to monitor the state of their Fledger nodes, +// view logs, and control the nodes remotely. +#[derive(Default, Clone, Debug)] +pub struct HermesApi { + client: reqwest::blocking::Client, +} + +impl HermesApi { + pub fn update(&self, state: SimulationState) -> Result { + let snapshot = Snapshot::make(state.clone()); + let url = format!("https://fledger.yohan.ch/api/nodes/{}", state.node_id); + let response = self.api_request(Method::PUT, url, &snapshot)?; + let bot_state: UpdateResponse = serde_json::from_str(&response)?; + Ok(bot_state) + } + + pub fn create_node(&self, experiment_id: u32, node_name: String) -> u32 { + let mut data = HashMap::new(); + data.insert("name", node_name.clone()); + let url = format!( + "https://fledger.yohan.ch/api/experiments/{}/nodes", + experiment_id + ); + + let text = self.api_request(Method::POST, url, &data).unwrap(); + let json: Value = serde_json::from_str(&text).unwrap(); + let id = json["id"].as_u64(); + if id.is_none() { + panic!("Failed to create node: no ID returned"); + } else { + id.unwrap() as u32 + } + } + + fn api_request( + &self, + method: Method, + url: String, + data: &T, + ) -> Result { + let start = Instant::now(); + match self + .client + .request(method, url) + .json(data) + .header("Accept", "application/json") + .header( + "Authorization", + "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", + ) + .send() + { + Ok(resp) => { + let text = resp.text()?; + log::info!( + "Successful API request in {}ms: {}", + start.elapsed().as_millis(), + text.clone() + ); + Ok(text) + } + Err(err) => { + log::error!("Error: {}", err); + Err(Error::new(err)) + } + } + } +} diff --git a/cli/fledger/src/hermes/mod.rs b/cli/fledger/src/hermes/mod.rs new file mode 100644 index 00000000..27057798 --- /dev/null +++ b/cli/fledger/src/hermes/mod.rs @@ -0,0 +1,3 @@ +pub mod api; +pub mod snapshot; +pub mod update_response; diff --git a/cli/fledger/src/hermes/snapshot.rs b/cli/fledger/src/hermes/snapshot.rs new file mode 100644 index 00000000..9e334e05 --- /dev/null +++ b/cli/fledger/src/hermes/snapshot.rs @@ -0,0 +1,98 @@ +use crate::state::SimulationState; +use serde::Serialize; + +#[derive(Serialize, Default, Debug, Clone)] +pub struct Snapshot { + node_status: String, + pages_stored: Vec, + evil_no_forward: bool, + target_page_id: Option, + + timed_metrics: Vec<(String, u32)>, + timeless_metrics: Vec<(String, u32)>, +} + +struct MetricsBuilder { + metrics: Vec<(String, u32)>, +} + +impl MetricsBuilder { + pub fn new() -> Self { + MetricsBuilder { + metrics: Vec::new(), + } + } + + pub fn add_metric(&mut self, key: String, value: u32) { + self.metrics.push((key, value)); + } + + pub fn build(self) -> Vec<(String, u32)> { + self.metrics.clone() + } +} + +impl Snapshot { + pub fn make(simulation_state: SimulationState) -> Self { + let mut timed_metrics = MetricsBuilder::new(); + timed_metrics.add_metric( + "pages_stored".to_owned(), + simulation_state.pages_stored.len() as u32, + ); + + let mut timeless_metrics = MetricsBuilder::new(); + timeless_metrics.add_metric( + "connected_nodes_total".to_string(), + simulation_state.connected_nodes_total, + ); + timeless_metrics.add_metric( + "pages_stored_total".to_string(), + simulation_state.pages_stored.len() as u32, + ); + timeless_metrics.add_metric( + "ds_size_bytes".to_string(), + simulation_state.ds_size_bytes as u32, + ); + timeless_metrics.add_metric( + "flos_sent_total".to_string(), + simulation_state.ds_metrics.max_flos_sent_in_flos, + ); + timeless_metrics.add_metric( + "available_flos_sent_total".to_string(), + simulation_state.ds_metrics.flo_value_sent_total, + ); + timeless_metrics.add_metric( + "available_flos_sent_blocked_total".to_string(), + simulation_state.ds_metrics.flo_value_sent_blocked_total, + ); + timeless_metrics.add_metric( + "max_flo_metas_received_in_available_flos".to_string(), + simulation_state + .ds_metrics + .max_flo_metas_received_in_available_flos, + ); + timeless_metrics.add_metric( + "max_flo_metas_requested_in_request_flos".to_string(), + simulation_state + .ds_metrics + .max_flo_metas_requested_in_request_flos, + ); + + if let Some(target_page_stored_bool) = simulation_state.target_page_stored_bool { + timeless_metrics.add_metric( + "target_page_stored_bool".to_string(), + target_page_stored_bool as u32, + ); + } + + Snapshot { + node_status: simulation_state.node_status, + pages_stored: simulation_state.pages_stored, + evil_no_forward: simulation_state.evil_no_forward, + target_page_id: simulation_state.target_page_id, + + timed_metrics: timed_metrics.build(), + timeless_metrics: timeless_metrics.build(), + } + } +} diff --git a/cli/fledger/src/hermes/update_response.rs b/cli/fledger/src/hermes/update_response.rs new file mode 100644 index 00000000..75799315 --- /dev/null +++ b/cli/fledger/src/hermes/update_response.rs @@ -0,0 +1,10 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct UpdateResponse { + pub target_page_id: Option, + pub timed_data_points: Vec, + pub timeless_data_points: Vec, +} + +impl UpdateResponse {} diff --git a/cli/fledger/src/api/influx.rs b/cli/fledger/src/influx/api.rs similarity index 88% rename from cli/fledger/src/api/influx.rs rename to cli/fledger/src/influx/api.rs index 4acc45d2..122116f8 100644 --- a/cli/fledger/src/api/influx.rs +++ b/cli/fledger/src/influx/api.rs @@ -1,17 +1,11 @@ use tokio::time::Instant; -#[derive(Clone)] +#[derive(Default, Debug, Clone)] pub struct InfluxApi { client: reqwest::blocking::Client, } impl InfluxApi { - pub fn new() -> Self { - InfluxApi { - client: reqwest::blocking::Client::new(), - } - } - pub fn write(&self, influx_data: String) -> Result { let start = Instant::now(); match self. diff --git a/cli/fledger/src/influx/lines.rs b/cli/fledger/src/influx/lines.rs new file mode 100644 index 00000000..f9016df3 --- /dev/null +++ b/cli/fledger/src/influx/lines.rs @@ -0,0 +1,95 @@ +use crate::state::SimulationState; + +// Influx Line Protocol for Fledger +pub struct InfluxLines { + node_name: String, +} + +impl InfluxLines { + fn create_influx_line(&self, measurement: String, value: u32) -> String { + format!("{measurement},node_name={} value={value}", self.node_name) + } + + pub fn make_influx_data(node_name: String, stats: SimulationState) -> String { + let generator = Self { node_name }; + + let mut lines = Vec::new(); + lines.push(generator.create_influx_line( + "fledger_realm_storage_bytes".to_string(), + stats.ds_size_bytes as u32, + )); + lines.push(generator.create_influx_line( + "fledger_pages_total".to_string(), + stats.pages_stored.len() as u32, + )); + lines.push(generator.create_influx_line( + "fledger_connected_total".to_string(), + stats.connected_nodes_total, + )); + // lines.push(generator.create_influx_line( + // "fledger_forwarded_flo_requests_total".to_string(), + // stats.request_flos_received_total, + // )); + lines.push(generator.create_influx_line( + "fledger_forwarded_flo_meta_requests_total".to_string(), + stats.ds_metrics.available_flos_sent_total, + )); + lines.push(generator.create_influx_line( + "fledger_blocked_flo_meta_requests_total".to_string(), + stats.ds_metrics.available_flos_sent_blocked_total, + )); + lines.push(generator.create_influx_line( + "fledger_flos_metas_received_from_neighbour".to_string(), + stats.ds_metrics.max_flo_metas_received_in_available_flos, + )); + lines.push(generator.create_influx_line( + "fledger_flos_requested_from_neighbour".to_string(), + stats.ds_metrics.max_flo_metas_requested_in_request_flos, + )); + lines.push(generator.create_influx_line( + "fledger_flos_ids_received_from_neighbour".to_string(), + stats.ds_metrics.max_flo_ids_received_in_request_flos, + )); + lines.push(generator.create_influx_line( + "fledger_forwarded_flo_requests_total".to_string(), + stats.ds_metrics.flos_sent_total, + )); + lines.push(generator.create_influx_line( + "fledger_flos_sent_to_neighbour".to_string(), + stats.ds_metrics.max_flos_sent_in_flos, + )); + lines.push(generator.create_influx_line( + "fledger_blocked_flo_requests_total".to_string(), + stats.ds_metrics.flos_sent_blocked_total, + )); + lines.push(generator.create_influx_line( + "fledger_flo_value_sent_total".to_string(), + stats.ds_metrics.flo_value_sent_total, + )); + lines.push(generator.create_influx_line( + "fledger_flo_value_blocked_total".to_string(), + stats.ds_metrics.flo_value_sent_blocked_total, + )); + lines.push(generator.create_influx_line( + "fledger_blocked_flo_requests_total".to_string(), + stats.ds_metrics.flos_sent_blocked_total, + )); + lines.push(generator.create_influx_line( + "fledger_flos_received_from_neighbour".to_string(), // received + stats.ds_metrics.max_flos_received_in_flos, + )); + lines.push(generator.create_influx_line( + "fledger_ds_store_flo_total".to_string(), + stats.ds_metrics.store_flo_total, + )); + + if let Some(target_page_stored_bool) = stats.target_page_stored_bool { + lines.push(generator.create_influx_line( + "fledger_simulation_dht_target_state".to_string(), + target_page_stored_bool as u32, + )); + } + + lines.join("\n") + } +} diff --git a/cli/fledger/src/influx/mod.rs b/cli/fledger/src/influx/mod.rs new file mode 100644 index 00000000..e6735683 --- /dev/null +++ b/cli/fledger/src/influx/mod.rs @@ -0,0 +1,2 @@ +pub mod api; +pub mod lines; diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 3956b135..03a16e2a 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -2,7 +2,6 @@ use clap::{Parser, Subcommand}; use flarch::{ data_storage::{DataStorage, DataStorageFile}, - random, tasks::wait_ms, web_rtc::connection::{ConnectionConfig, HostLogin}, }; @@ -12,20 +11,19 @@ use flmodules::{ network::{broker::NetworkIn, network_start, signal::SIGNAL_VERSION}, }; use flnode::{node::Node, version::VERSION_STRING}; -use ::metrics::absolute_counter; -use metrics::Metrics; use page::{Page, PageCommands}; use realm::{RealmCommands, RealmHandler}; use simulation::{SimulationCommand, SimulationHandler}; -mod api; -mod metrics; +mod hermes; +mod influx; mod page; mod realm; mod simulation; mod simulation_chat; mod simulation_dht_target; mod simulation_realm; +mod state; /// Fledger node CLI binary #[derive(Parser, Debug, Clone)] @@ -154,11 +152,6 @@ async fn main() -> anyhow::Result<()> { flmodules::dht_router::messages::EVIL_NO_FORWARD = args.evil_noforward; } - absolute_counter!( - "fledger_evil_noforward", - if args.evil_noforward { 1 } else { 0 } - ); - Fledger::run(args).await } @@ -178,10 +171,6 @@ impl Fledger { .as_ref() .map(|name| node_config.info.name = name.clone()); - // necessary to grab the variable for lifetime purposes. - let node_name = args.name.clone().unwrap_or("unknown".into()); - let _influx = Metrics::setup(node_name); - let cc = if args.disable_turn_stun { ConnectionConfig::from_signal(&args.signal_url) } else { @@ -220,6 +209,7 @@ impl Fledger { Commands::Stats {} => todo!(), Commands::Page { command } => { Page::run(Self::make_fledger(args).await?, command).await + } Commands::Simulation(command) => { SimulationHandler::run(Self::make_fledger(args).await?, command).await } @@ -251,16 +241,6 @@ impl Fledger { wait_ms(1000).await; - absolute_counter!("fledger_iterations_total", count as u64); - - if !self.ds.stats.borrow().realm_stats.is_empty() { - let allstats = self.ds.stats.borrow(); - let stats = allstats.realm_stats.iter().next().unwrap().1; - - absolute_counter!("dht_storage_flos_total", stats.flos as u64); - absolute_counter!("dht_storage_size_bytes", stats.size as u64) - } - if match state { FledgerState::Connected(i) => self.dr.stats.borrow().active >= i, FledgerState::DHTAvailable => !self.ds.stats.borrow().realm_stats.is_empty(), diff --git a/cli/fledger/src/metrics.rs b/cli/fledger/src/metrics.rs deleted file mode 100644 index 9413cdd8..00000000 --- a/cli/fledger/src/metrics.rs +++ /dev/null @@ -1,363 +0,0 @@ -use std::{collections::HashMap, fs::File, time::Duration}; - -use crate::api::influx::InfluxApi; -use crate::simulation_dht_target::stats::SimulationStats; -use anyhow::Error; -use metrics_exporter_influx::{InfluxBuilder, InfluxRecorderHandle}; -use serde_json::Value; -use tokio::time::Instant; - -#[derive(Clone)] -pub struct Metrics { - experiment_id: u32, - node_name: String, - node_id: Option, - - client: reqwest::blocking::Client, - influx_api: InfluxApi, -} - -impl Metrics { - pub fn new(experiment_id: u32, node_name: String) -> Self { - let mut metrics = Self { - experiment_id, - node_name, - node_id: None, - client: reqwest::blocking::Client::new(), - influx_api: InfluxApi::new(), - }; - - let _ = metrics.api_create_node(); - return metrics.clone(); - } - - pub fn setup(node_name: String) -> InfluxRecorderHandle { - log::info!("Setting up metrics"); - let metrics_file = File::create(format!("/tmp/{}.metrics", node_name)) - .expect(format!("could not create /tmp/{}.metrics", node_name).as_ref()); - return InfluxBuilder::new() - .with_duration(Duration::from_secs(10)) - .with_writer(metrics_file) - .add_global_tag("node_name", node_name) - .install() - .expect("could not setup influx recorder"); - } - - pub fn upload(&self, stats: SimulationStats) { - let mut data = HashMap::new(); - data.insert("pages", stats.pages.clone()); - self.api_put_data_node(data); - - let mut datapoints = HashMap::new(); - datapoints.insert( - "connected_nodes_total", - stats.connected_nodes_total.to_string(), - ); - self.api_post_datapoints(datapoints); - - let mut timeless_datapoints = HashMap::new(); - timeless_datapoints.insert( - "target_page_stored_bool", - stats.target_page_stored_bool.to_string(), - ); - timeless_datapoints.insert( - "connected_nodes_total", - stats.connected_nodes_total.to_string(), - ); - timeless_datapoints.insert("pages_stored_total", stats.pages_stored_total.to_string()); - timeless_datapoints.insert("ds_size_bytes", stats.ds_size_bytes.to_string()); - - timeless_datapoints.insert( - "flos_sent_total", - stats.ds_experiment_stats.max_flos_sent_in_flos.to_string(), - ); - timeless_datapoints.insert( - "available_flos_sent_total", - stats.ds_experiment_stats.flo_value_sent_total.to_string(), - ); - timeless_datapoints.insert( - "available_flos_sent_blocked_total", - stats - .ds_experiment_stats - .flo_value_sent_blocked_total - .to_string(), - ); - timeless_datapoints.insert( - "max_flo_metas_received_in_available_flos", - stats - .ds_experiment_stats - .max_flo_metas_received_in_available_flos - .to_string(), - ); - timeless_datapoints.insert( - "max_flo_metas_requested_in_request_flos", - stats - .ds_experiment_stats - .max_flo_metas_requested_in_request_flos - .to_string(), - ); - self.api_post_timeless_datapoints(timeless_datapoints); - - self.influx_write(stats) - } - - pub fn upload_target_page_id(&self, target_page_id: String) { - let mut data = HashMap::new(); - data.insert("target_page_id", target_page_id); - self.api_put_data_experiment(data); - } - - pub fn pull_page_id(&self) -> Option { - match self - .client - .get(format!( - "https://fledger.yohan.ch/api/experiments/{}/target-page-id", - self.experiment_id - )) - .header("Accept", "application/json") - .header( - "Authorization", - "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", - ) - .send() - { - Ok(resp) => { - if resp.status().is_success() { - let text = resp.text().unwrap(); - log::info!("Successfully pulled page id: {}", text); - let response_data: Value = serde_json::from_str(&text).unwrap(); - Some( - response_data["target_page_id"] - .as_str() - .unwrap() - .to_string(), - ) - } else { - log::error!("Failed to pull page id: {}", resp.status()); - None - } - } - Err(err) => { - log::error!("Error pulling page id: {}", err); - None - } - } - } - - pub fn timeout(&self) { - let mut data = HashMap::new(); - data.insert("status", "timeout".to_string()); - //absolute_counter!("fledger_simulation_timeout", 1); - self.api_put_data_node(data); - } - - pub fn success(&self) { - let mut data = HashMap::new(); - data.insert("status", "success".to_string()); - //absolute_counter!("fledger_simulation_success", 1); - self.api_put_data_node(data); - } - - pub fn api_put_data_node(&self, data: HashMap<&str, String>) { - self.api_put_data( - format!( - "https://fledger.yohan.ch/api/nodes/{}", - self.node_id.clone().unwrap() - ), - data, - ); - } - - pub fn api_put_data_experiment(&self, data: HashMap<&str, String>) { - self.api_put_data( - format!( - "https://fledger.yohan.ch/api/experiments/{}", - self.experiment_id - ), - data, - ); - } - - pub fn api_put_data(&self, url: String, data: HashMap<&str, String>) { - let start = Instant::now(); - match reqwest::blocking::Client::new() - .put(url) - .json(&data) - .header("Accept", "application/json") - .header( - "Authorization", - "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", - ) - .send() - { - Ok(resp) => log::info!( - "Successful API request in {}ms: {}", - start.elapsed().as_millis(), - resp.text().unwrap() - ), - Err(err) => log::error!("Error: {}", err), - }; - } - - pub fn api_post_timeless_datapoints(&self, data: HashMap<&str, String>) { - self.api_post_nodes("timeless-data-points".to_string(), data); - } - - pub fn api_post_datapoints(&self, data: HashMap<&str, String>) { - self.api_post_nodes("data-points".to_string(), data); - } - - pub fn api_post_nodes(&self, resource: String, data: HashMap<&str, String>) { - let start = Instant::now(); - match reqwest::blocking::Client::new() - .post(format!( - "https://fledger.yohan.ch/api/nodes/{}/{resource}", - self.node_id.clone().unwrap() - )) - .json(&data) - .header("Accept", "application/json") - .header( - "Authorization", - "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", - ) - .send() - { - Ok(resp) => log::info!( - "Successful API request in {}ms ({resource}): {}", - start.elapsed().as_millis(), - resp.text().unwrap() - ), - Err(err) => log::error!("Error: {}", err), - }; - } - - fn api_create_node(&mut self) -> Result<(), Error> { - let mut data = HashMap::new(); - data.insert("name", self.node_name.clone()); - - match self - .client - .post(format!( - "https://fledger.yohan.ch/api/experiments/{}/nodes/", - self.experiment_id - )) - .json(&data) - .header("Accept", "application/json") - .header( - "Authorization", - "Bearer 1|d4EeHkRPlqwpgLpALyTor5FxHI4NWg1LXJtf5NZBfd82aa17", - ) - .send() - { - Ok(resp) => { - let text = resp.text().unwrap(); - log::info!("Successfully created node in API: {}", text.clone()); - let response_data: Value = serde_json::from_str(&text.clone())?; - self.node_id = Some(response_data["id"].to_string()); - return Ok(()); - } - Err(err) => { - log::error!("Error: {}", err); - return Err(err.into()); - } - }; - } - - fn create_influx_line(&self, measurement: String, value: u32) -> String { - let node_name = self.node_name.clone(); - format!("{measurement},node_name={node_name} value={value}") - } - - fn make_influx_data(&self, stats: SimulationStats) -> String { - let mut lines = Vec::new(); - lines.push(self.create_influx_line( - "fledger_realm_storage_bytes".to_string(), - stats.ds_size_bytes as u32, - )); - lines.push( - self.create_influx_line("fledger_pages_total".to_string(), stats.pages_stored_total), - ); - lines.push(self.create_influx_line( - "fledger_simulation_page_stored".to_string(), - stats.target_page_stored_bool as u32, - )); - lines.push(self.create_influx_line( - "fledger_connected_total".to_string(), - stats.connected_nodes_total, - )); - lines.push(self.create_influx_line( - "fledger_forwarded_flo_requests_total".to_string(), - stats.request_flos_received_total, - )); - lines.push(self.create_influx_line( - "fledger_forwarded_flo_meta_requests_total".to_string(), - stats.ds_experiment_stats.available_flos_sent_total, - )); - lines.push(self.create_influx_line( - "fledger_blocked_flo_meta_requests_total".to_string(), - stats.ds_experiment_stats.available_flos_sent_blocked_total, - )); - lines.push( - self.create_influx_line( - "fledger_flos_metas_received_from_neighbour".to_string(), - stats - .ds_experiment_stats - .max_flo_metas_received_in_available_flos, - ), - ); - lines.push( - self.create_influx_line( - "fledger_flos_requested_from_neighbour".to_string(), - stats - .ds_experiment_stats - .max_flo_metas_requested_in_request_flos, - ), - ); - lines.push( - self.create_influx_line( - "fledger_flos_ids_received_from_neighbour".to_string(), - stats - .ds_experiment_stats - .max_flo_ids_received_in_request_flos, - ), - ); - lines.push(self.create_influx_line( - "fledger_forwarded_flo_requests_total".to_string(), - stats.ds_experiment_stats.flos_sent_total, - )); - lines.push(self.create_influx_line( - "fledger_flos_sent_to_neighbour".to_string(), - stats.ds_experiment_stats.max_flos_sent_in_flos, - )); - lines.push(self.create_influx_line( - "fledger_blocked_flo_requests_total".to_string(), - stats.ds_experiment_stats.flos_sent_blocked_total, - )); - lines.push(self.create_influx_line( - "fledger_flo_value_sent_total".to_string(), - stats.ds_experiment_stats.flo_value_sent_total, - )); - lines.push(self.create_influx_line( - "fledger_flo_value_blocked_total".to_string(), - stats.ds_experiment_stats.flo_value_sent_blocked_total, - )); - lines.push(self.create_influx_line( - "fledger_blocked_flo_requests_total".to_string(), - stats.ds_experiment_stats.flos_sent_blocked_total, - )); - lines.push(self.create_influx_line( - "fledger_flos_received_from_neighbour".to_string(), // received - stats.ds_experiment_stats.max_flos_received_in_flos, - )); - lines.push(self.create_influx_line( - "fledger_ds_store_flo_total".to_string(), - stats.ds_experiment_stats.store_flo_total, - )); - - lines.join("\n") - } - - fn influx_write(&self, stats: SimulationStats) -> () { - let _ = self.influx_api.write(self.make_influx_data(stats)); - } -} diff --git a/cli/fledger/src/simulation_chat/simulation.rs b/cli/fledger/src/simulation_chat/simulation.rs index bca6337c..68f6b3d8 100644 --- a/cli/fledger/src/simulation_chat/simulation.rs +++ b/cli/fledger/src/simulation_chat/simulation.rs @@ -3,7 +3,6 @@ use crate::Fledger; use flarch::nodeids::U256; use flarch::tasks::wait_ms; use flmodules::gossip_events::core::Event; -use metrics::{absolute_counter, increment_counter}; #[derive(Clone)] pub struct SimulationChat {} @@ -31,15 +30,6 @@ impl SimulationChat { loop { wait_ms(1000).await; - let fledger_message_total = f.node.gossip.as_ref().unwrap().chat_events().len(); - let fledger_connected_total = f.node.nodes_connected()?.len(); - absolute_counter!( - "fledger_message_total", - fledger_message_total.try_into().unwrap() - ); - absolute_counter!("fledger_connected_total", fledger_connected_total as u64); - increment_counter!("fledger_iterations_total"); - if simulation_args.print_new_messages { Self::log_new_messages(&f, &mut acked_msg_ids); } diff --git a/cli/fledger/src/simulation_dht_target/mod.rs b/cli/fledger/src/simulation_dht_target/mod.rs index 512630f4..47a12cbb 100644 --- a/cli/fledger/src/simulation_dht_target/mod.rs +++ b/cli/fledger/src/simulation_dht_target/mod.rs @@ -1,2 +1 @@ pub mod simulation; -pub mod stats; diff --git a/cli/fledger/src/simulation_dht_target/simulation.rs b/cli/fledger/src/simulation_dht_target/simulation.rs index 4c45b4ac..f8922491 100644 --- a/cli/fledger/src/simulation_dht_target/simulation.rs +++ b/cli/fledger/src/simulation_dht_target/simulation.rs @@ -12,8 +12,8 @@ use flmodules::{ use std::str::FromStr; use std::time::{Duration, Instant}; -use crate::simulation_dht_target::stats::SimulationStats; -use crate::{metrics::Metrics, Fledger}; +use crate::state::SimulationState; +use crate::Fledger; #[derive(Clone)] pub struct SimulationDhtTarget {} @@ -27,7 +27,7 @@ impl SimulationDhtTarget { connection_delay: u32, experiment_id: u32, ) -> anyhow::Result<()> { - let metrics = Metrics::new(experiment_id, f.node.node_config.info.name.clone()); + let mut state = SimulationState::new(experiment_id, f.node.node_config.info.name.clone()); f.loop_node(crate::FledgerState::DHTAvailable).await?; log::info!("DHT CONNECTED"); @@ -110,7 +110,8 @@ impl SimulationDhtTarget { .unwrap() .store_flo(flo_page.flo().clone())?; - metrics.upload_target_page_id(flo_page.flo_id().to_string()); + state.target_page_id = Some(flo_page.flo_id().to_string()); + state.update_and_upload(&mut f).await; wait_ms(1000).await; @@ -125,7 +126,8 @@ impl SimulationDhtTarget { f.node.dht_storage.as_mut().unwrap().sync()?; log::info!("SIMULATION END"); - metrics.success(); + state.success(); + state.update_and_upload(&mut f).await; f.loop_node(crate::FledgerState::Forever).await?; Ok(()) @@ -140,8 +142,8 @@ impl SimulationDhtTarget { ) -> anyhow::Result<()> { let start_instant = Instant::now(); - let metrics = Metrics::new(experiment_id, f.node.node_config.info.name.clone()); - let mut simulation_metrics = SimulationStats::new(); + let node_name = f.node.node_config.info.name.clone(); + let mut state = SimulationState::new(experiment_id, node_name); let timeout_result = timeout( Duration::from_millis(timeout_ms.into()), @@ -152,7 +154,7 @@ impl SimulationDhtTarget { if timeout_result.is_err() { log::warn!("SIMULATION TIMEOUT WHILE CONNECTING TO DHT"); log::info!("SIMULATION END"); - metrics.timeout(); + state.timeout(); return Err(timeout_result.unwrap_err().into()); } @@ -172,7 +174,8 @@ impl SimulationDhtTarget { if start_instant.elapsed().as_millis() > timeout_ms as u128 { log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); log::info!("SIMULATION END"); - metrics.timeout(); + state.timeout(); + state.update_and_upload(&mut f).await; f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); @@ -190,18 +193,15 @@ impl SimulationDhtTarget { // .inspect_err(|e| log::error!("error when doing rv.update_all(): {e}")); if iteration % 10 == 0 { - simulation_metrics.refresh(&mut f).await; - metrics.upload(simulation_metrics.clone()); + let response = state.update_and_upload(&mut f).await; + page_id_opt = response.target_page_id; } // Stop here each iteration // until page id is known if page_id_opt.is_none() { - page_id_opt = metrics.pull_page_id(); - if page_id_opt.is_none() { - log::info!("failed to get page id"); - continue; - } + log::info!("failed to get page id"); + continue; } // todo!("test this"); @@ -241,9 +241,8 @@ impl SimulationDhtTarget { ); log::info!("SIMULATION END"); - simulation_metrics.refresh(&mut f).await; - metrics.upload(simulation_metrics.clone()); - metrics.success(); + state.success(); + state.update_and_upload(&mut f).await; f.loop_node(crate::FledgerState::Forever).await?; return Ok(()); diff --git a/cli/fledger/src/simulation_dht_target/stats.rs b/cli/fledger/src/simulation_dht_target/stats.rs deleted file mode 100644 index b60375a0..00000000 --- a/cli/fledger/src/simulation_dht_target/stats.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::Fledger; -use flcrypto::tofrombytes::ToFromBytes; -use flmodules::dht_storage::broker::DHTStorage; -use flmodules::dht_storage::messages::ExperimentStats; -use flmodules::flo::blob::{BlobAccess, BlobPage}; -use metrics::absolute_counter; -use std::any::type_name; - -#[derive(Clone)] -pub struct SimulationStats { - pub pages: String, - pub target_page_stored_bool: u8, - pub connected_nodes_total: u32, - pub pages_stored_total: u32, - pub ds_size_bytes: u64, - - pub request_flos_received_total: u32, - pub ds_experiment_stats: ExperimentStats, -} - -impl SimulationStats { - pub fn new() -> Self { - Self { - pages: "".to_string(), - target_page_stored_bool: 0, - connected_nodes_total: 0, - pages_stored_total: 0, - ds_size_bytes: 0, - - request_flos_received_total: 0, - ds_experiment_stats: ExperimentStats::new(), - } - } - - pub async fn refresh_pages(&mut self, ds: &mut DHTStorage) { - let pages = ds - .get_flos() - .await - .unwrap_or_else(|e| { - log::error!("failed to get flos {e}"); - vec![] - }) - .clone(); - let pages = pages - .iter() - .filter(|flo| flo.flo_type() == type_name::()) - .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()); - - let page_names = pages - .clone() - .map(|page| page.0.values().iter().next().unwrap().1.clone()); - - let simulation_page_stored_this_iteration = page_names - .clone() - .find(|name| name == "simulation-page") - .is_some(); - - let page_list = page_names - .clone() - .map(|name| { - return name.replace("simulation-filler-", ""); - }) - .collect::>(); - - let pages_csv = page_list.clone().join(", "); - - self.pages = pages_csv.clone(); - self.target_page_stored_bool = if simulation_page_stored_this_iteration { - 1 - } else { - 0 - }; - self.pages_stored_total = page_list.len() as u32; - - log::info!("pages stored: {pages_csv}"); - } - - pub async fn refresh(&mut self, f: &mut Fledger) { - let connected_nodes_total = f - .node - .dht_router - .clone() - .unwrap() - .stats - .borrow() - .active - .clone() as u32; - let ds = f.node.dht_storage.as_mut().unwrap(); - let ds_size = ds.stats.borrow().realm_stats.iter().next().unwrap().1.size; - self.ds_experiment_stats = ds.stats.borrow().experiment_stats.clone(); - self.connected_nodes_total = connected_nodes_total; - self.ds_size_bytes = ds_size; - - self.refresh_pages(ds).await; - - self.dump_influx(); - } - - pub fn dump_influx(&self) { - absolute_counter!("fledger_realm_storage_bytes", self.ds_size_bytes); - absolute_counter!("fledger_pages_total", self.pages_stored_total as u64); - absolute_counter!( - "fledger_simulation_page_stored", - self.target_page_stored_bool as u64 - ); - absolute_counter!("fledger_connected_total", self.connected_nodes_total as u64); - absolute_counter!( - "fledger_forwarded_flo_requests_total", - self.request_flos_received_total as u64 - ); - absolute_counter!( - "fledger_forwarded_flo_meta_requests_total", - self.ds_experiment_stats.available_flos_sent_total as u64 - ); - absolute_counter!( - "fledger_blocked_flo_meta_requests_total", - self.ds_experiment_stats.available_flos_sent_blocked_total as u64 - ); - absolute_counter!( - "fledger_flos_metas_received_from_neighbour", - self.ds_experiment_stats - .max_flo_metas_received_in_available_flos as u64 - ); - absolute_counter!( - "fledger_flos_requested_from_neighbour", - self.ds_experiment_stats - .max_flo_metas_requested_in_request_flos as u64 - ); - absolute_counter!( - "fledger_flos_ids_received_from_neighbour", - self.ds_experiment_stats - .max_flo_ids_received_in_request_flos as u64 - ); - absolute_counter!( - "fledger_forwarded_flo_requests_total", - self.ds_experiment_stats.flos_sent_total as u64 - ); - absolute_counter!( - "fledger_flos_sent_to_neighbour", - self.ds_experiment_stats.max_flos_sent_in_flos as u64 - ); - absolute_counter!( - "fledger_blocked_flo_requests_total", - self.ds_experiment_stats.flos_sent_blocked_total as u64 - ); - absolute_counter!( - "fledger_flo_value_sent_total", - self.ds_experiment_stats.flo_value_sent_total as u64 - ); - absolute_counter!( - "fledger_flo_value_blocked_total", - self.ds_experiment_stats.flo_value_sent_blocked_total as u64 - ); - absolute_counter!( - "fledger_blocked_flo_requests_total", - self.ds_experiment_stats.flos_sent_blocked_total as u64 - ); - absolute_counter!( - "fledger_flos_received_from_neighbour", // received - self.ds_experiment_stats.max_flos_received_in_flos as u64 - ); - absolute_counter!( - "fledger_ds_store_flo_total", - self.ds_experiment_stats.store_flo_total as u64 - ); - } -} diff --git a/cli/fledger/src/simulation_realm/simulation.rs b/cli/fledger/src/simulation_realm/simulation.rs index 158bee70..0b81434f 100644 --- a/cli/fledger/src/simulation_realm/simulation.rs +++ b/cli/fledger/src/simulation_realm/simulation.rs @@ -1,5 +1,4 @@ use crate::Fledger; -use metrics::absolute_counter; #[derive(Clone)] pub struct SimulationRealm {} @@ -9,9 +8,7 @@ impl SimulationRealm { f.loop_node(crate::FledgerState::DHTAvailable).await?; log::info!("SIMULATION END"); - absolute_counter!("fledger_realms_total", 1); - f.loop_node(crate::FledgerState::Forever).await?; - return Ok(()); + Ok(()) } } diff --git a/cli/fledger/src/state.rs b/cli/fledger/src/state.rs new file mode 100644 index 00000000..ead5c53d --- /dev/null +++ b/cli/fledger/src/state.rs @@ -0,0 +1,111 @@ +use crate::hermes::api::HermesApi; +use crate::hermes::update_response::UpdateResponse; +use crate::influx::api::InfluxApi; +use crate::influx::lines::InfluxLines; +use crate::Fledger; +use flcrypto::tofrombytes::ToFromBytes; +use flmodules::dht_storage::broker::DHTStorage; +use flmodules::dht_storage::messages::DsMetrics; +use flmodules::flo::blob::{BlobAccess, BlobPage}; +use std::any::type_name; + +#[derive(Clone, Debug, Default)] +pub struct SimulationState { + pub experiment_id: u32, + pub node_id: u32, + pub node_name: String, + pub node_status: String, + + pub pages_stored: Vec, + pub connected_nodes_total: u32, + pub ds_size_bytes: u64, + pub evil_no_forward: bool, + + pub ds_metrics: DsMetrics, + + // simulation_dht_target specific fields + pub target_page_stored_bool: Option, + pub target_page_id: Option, + + pub api: HermesApi, + pub influx: InfluxApi, +} + +impl SimulationState { + pub fn new(experiment_id: u32, node_name: String) -> Self { + let mut state = Self::default(); + + let node_id = state.api.create_node(experiment_id, node_name.clone()); + + state.experiment_id = experiment_id; + state.node_id = node_id; + state.node_name = node_name; + state.node_status = "active".to_string(); + state + } + + pub async fn refresh_pages(&mut self, ds: &mut DHTStorage) { + let pages_stored = ds + .get_flos() + .await + .unwrap_or_else(|e| { + log::error!("failed to get flos {e}"); + vec![] + }) + .iter() + .filter(|flo| flo.flo_type() == type_name::()) + .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()) + .map(|page| page.0.values().iter().next().unwrap().1.clone()) + .map(|name| { + return name.replace("simulation-filler-", ""); + }) + .collect::>(); + + if pages_stored.contains(&"simulation-page".to_string()) { + self.target_page_stored_bool = Some(true); + } + + self.pages_stored = pages_stored.clone(); + log::info!("pages stored: {}", pages_stored.join(", ")); + } + + pub fn success(&mut self) { + self.node_status = "success".to_string(); + } + + pub fn timeout(&mut self) { + self.node_status = "timeout".to_string(); + } + + pub async fn update_and_upload(&mut self, f: &mut Fledger) -> UpdateResponse { + let connected_nodes_total = f + .node + .dht_router + .clone() + .unwrap() + .stats + .borrow() + .active + .clone() as u32; + let ds = f.node.dht_storage.as_mut().unwrap(); + let ds_size = ds.stats.borrow().realm_stats.iter().next().unwrap().1.size; + self.ds_metrics = ds.stats.borrow().experiment_stats.clone(); + self.connected_nodes_total = connected_nodes_total; + self.ds_size_bytes = ds_size; + self.refresh_pages(ds).await; + + self.upload() + } + + pub fn upload(&mut self) -> UpdateResponse { + let node_name = self.node_name.clone(); + let _ = self + .influx + .write(InfluxLines::make_influx_data(node_name, self.clone())); + + self.api.update(self.clone()).unwrap_or_else(|e| { + log::error!("failed to update simulation state: {e}"); + UpdateResponse::default() + }) + } +} diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 47e9b8e0..dc06e53f 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -87,8 +87,8 @@ pub struct RealmStats { pub config: RealmConfig, } -#[derive(Debug, Default, Clone)] -pub struct ExperimentStats { +#[derive(Serialize, Debug, Default, Clone)] +pub struct DsMetrics { pub store_flo_total: u32, pub flo_value_sent_total: u32, pub flo_value_sent_blocked_total: u32, @@ -138,7 +138,7 @@ macro_rules! max_stat { pub struct Stats { pub realm_stats: HashMap, pub system_realms: Vec, - pub experiment_stats: ExperimentStats, + pub experiment_stats: DsMetrics, } /// The message handling part, but only for DHTStorage messages. @@ -150,7 +150,7 @@ pub struct Messages { ds: Box, tx: Option>, - experiment_stats: ExperimentStats, + experiment_stats: DsMetrics, } pub static mut EVIL_NO_FORWARD: bool = false; @@ -173,7 +173,7 @@ impl Messages { ds, tx: Some(tx), - experiment_stats: ExperimentStats::new(), + experiment_stats: DsMetrics::new(), }; msgs.store(); (msgs, rx) @@ -589,7 +589,7 @@ impl SubsystemHandler for Messages { } } -impl ExperimentStats { +impl DsMetrics { pub fn new() -> Self { Self::default() } @@ -599,7 +599,7 @@ impl Stats { fn from_realms( realms: &HashMap, system_realms: Vec, - experiment_stats: ExperimentStats, + experiment_stats: DsMetrics, ) -> Self { Self { realm_stats: realms From edb7893c5a5964ccfe444e69cb7eca3a796b45f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Tue, 10 Jun 2025 22:42:27 +0200 Subject: [PATCH 38/43] fix: from meeting 2025-06-10 - malicious nodes become malicious only after target page is propagated - 500ms between each filler page creation - no limit in `Flos` messages - fixed some metrics --- cli/fledger/src/hermes/snapshot.rs | 48 +++++- cli/fledger/src/main.rs | 5 - cli/fledger/src/simulation.rs | 2 + .../src/simulation_dht_target/simulation.rs | 142 +++++++++--------- cli/fledger/src/state.rs | 8 + flmodules/src/dht_storage/messages.rs | 20 +-- 6 files changed, 129 insertions(+), 96 deletions(-) diff --git a/cli/fledger/src/hermes/snapshot.rs b/cli/fledger/src/hermes/snapshot.rs index 9e334e05..cbeeab91 100644 --- a/cli/fledger/src/hermes/snapshot.rs +++ b/cli/fledger/src/hermes/snapshot.rs @@ -54,17 +54,43 @@ impl Snapshot { simulation_state.ds_size_bytes as u32, ); timeless_metrics.add_metric( - "flos_sent_total".to_string(), - simulation_state.ds_metrics.max_flos_sent_in_flos, + "evil_no_forward".to_string(), + simulation_state.evil_no_forward as u32, ); timeless_metrics.add_metric( - "available_flos_sent_total".to_string(), + "store_flo_total".to_string(), + simulation_state.ds_metrics.store_flo_total, + ); + timeless_metrics.add_metric( + "request_flo_metas_sent_total".to_string(), + simulation_state.ds_metrics.request_flo_metas_sent_total, + ); + timeless_metrics.add_metric( + "flo_value_sent_total".to_string(), simulation_state.ds_metrics.flo_value_sent_total, ); timeless_metrics.add_metric( - "available_flos_sent_blocked_total".to_string(), + "flo_value_sent_blocked_total".to_string(), simulation_state.ds_metrics.flo_value_sent_blocked_total, ); + timeless_metrics.add_metric( + "available_flos_sent_total".to_string(), + simulation_state.ds_metrics.available_flos_sent_total, + ); + timeless_metrics.add_metric( + "available_flos_sent_blocked_total".to_string(), + simulation_state + .ds_metrics + .available_flos_sent_blocked_total, + ); + timeless_metrics.add_metric( + "flos_sent_total".to_string(), + simulation_state.ds_metrics.flos_sent_total, + ); + timeless_metrics.add_metric( + "flos_sent_blocked_total".to_string(), + simulation_state.ds_metrics.flos_sent_blocked_total, + ); timeless_metrics.add_metric( "max_flo_metas_received_in_available_flos".to_string(), simulation_state @@ -77,6 +103,20 @@ impl Snapshot { .ds_metrics .max_flo_metas_requested_in_request_flos, ); + timeless_metrics.add_metric( + "max_flo_ids_received_in_request_flos".to_string(), + simulation_state + .ds_metrics + .max_flo_ids_received_in_request_flos, + ); + timeless_metrics.add_metric( + "max_flos_sent_in_flos".to_string(), + simulation_state.ds_metrics.max_flos_sent_in_flos, + ); + timeless_metrics.add_metric( + "max_flos_received_in_flos".to_string(), + simulation_state.ds_metrics.max_flos_received_in_flos, + ); if let Some(target_page_stored_bool) = simulation_state.target_page_stored_bool { timeless_metrics.add_metric( diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 03a16e2a..4a764fd1 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -147,11 +147,6 @@ async fn main() -> anyhow::Result<()> { VERSION_STRING ); - unsafe { - flmodules::dht_storage::messages::EVIL_NO_FORWARD = args.evil_noforward; - flmodules::dht_router::messages::EVIL_NO_FORWARD = args.evil_noforward; - } - Fledger::run(args).await } diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 1f246e60..d3e7ce07 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -99,12 +99,14 @@ impl SimulationHandler { enable_sync, experiment_id, } => { + let evil_noforward = f.args.evil_noforward.clone(); SimulationDhtTarget::fetch_target( f, loop_delay, enable_sync, timeout_ms, experiment_id, + evil_noforward, ) .await } diff --git a/cli/fledger/src/simulation_dht_target/simulation.rs b/cli/fledger/src/simulation_dht_target/simulation.rs index f8922491..746dbe0f 100644 --- a/cli/fledger/src/simulation_dht_target/simulation.rs +++ b/cli/fledger/src/simulation_dht_target/simulation.rs @@ -19,6 +19,47 @@ use crate::Fledger; pub struct SimulationDhtTarget {} impl SimulationDhtTarget { + fn log_page_info(flo_page: &FloWrapper) { + let page_content = + String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()) + .unwrap_or_default(); + log::info!( + "page {}/{}/{} | {} | {} ({}B -> {}B)", + flo_page.flo_id(), + flo_page.realm_id(), + flo_page.version(), + flo_page.values().iter().next().unwrap().1, + page_content.chars().take(50).collect::(), + page_content.size(), + flo_page.size(), + ); + } + + async fn create_flo_page( + rv: &mut RealmView, + name: &str, + content: String, + ) -> anyhow::Result> { + let flo_page = rv + .create_http(name, content, None, flcrypto::access::Condition::Pass, &[]) + .await?; + Self::log_page_info(&flo_page); + Ok(flo_page) + } + + async fn settle_and_sync(f: &mut Fledger) -> anyhow::Result<()> { + // ds.settle, ds.sync + f.node + .dht_storage + .as_mut() + .unwrap() + .broker + .settle(Vec::new()) + .await?; + f.node.dht_storage.as_mut().unwrap().sync()?; + Ok(()) + } + pub async fn run_create_fillers_and_target( mut f: Fledger, filler_amount: u32, @@ -41,89 +82,39 @@ impl SimulationDhtTarget { log::info!("[Create filler pages]"); for i in 0..filler_amount { - let page_content = String::from_utf8(vec![b'-'; page_size as usize])?; - let flo_page = rv - .create_http( - &format!("simulation-filler-{}", i.to_string()), - page_content.clone(), - None, - flcrypto::access::Condition::Pass, - &[], - ) - .await - .unwrap(); - - log::info!( - "page {}/{}/{} | {} | {} ({}B -> {}B)", - flo_page.flo_id(), - flo_page.realm_id(), - flo_page.version(), - flo_page.values().iter().next().unwrap().1, - page_content.clone().chars().take(50).collect::(), - page_content.size(), - flo_page.size(), - ); + wait_ms(500).await; + Self::create_flo_page( + &mut rv, + &format!("simulation-filler-{}", i.to_string()), + String::from_utf8(vec![b'-'; page_size as usize])?, + ) + .await?; } log::info!("[Waiting for fillers to settle]"); log::info!("{} ms", pages_propagation_delay); - // ds.broker.settle, ds.sync - f.node - .dht_storage - .as_mut() - .unwrap() - .broker - .settle(Vec::new()) - .await?; - f.node.dht_storage.as_mut().unwrap().sync()?; + Self::settle_and_sync(&mut f).await?; wait_ms(pages_propagation_delay as u64).await; log::info!("[Sending simulation flo page]"); - let flo_page = rv - .create_http( - "simulation-page", - String::from_utf8(vec![b'o'; page_size as usize])?, - None, - flcrypto::access::Condition::Pass, - &[], - ) - .await?; - - let page_content = - String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()).unwrap(); - - log::info!( - "page {}/{}/{} | {} | {} ({}B -> {}B)", - flo_page.flo_id(), - flo_page.realm_id(), - flo_page.version(), - flo_page.values().iter().next().unwrap().1, - page_content.chars().take(50).collect::(), - page_content.size(), - flo_page.size(), - ); + let flo_page = Self::create_flo_page( + &mut rv, + "simulation-page", + String::from_utf8(vec![b'o'; page_size as usize])?, + ) + .await?; - f.node - .dht_storage - .as_mut() - .unwrap() - .store_flo(flo_page.flo().clone())?; + log::info!("[Waiting for target page to propagate]"); + log::info!("5000 ms"); + wait_ms(5000).await; state.target_page_id = Some(flo_page.flo_id().to_string()); state.update_and_upload(&mut f).await; wait_ms(1000).await; - // ds.broker.settle, ds.sync - f.node - .dht_storage - .as_mut() - .unwrap() - .broker - .settle(Vec::new()) - .await?; - f.node.dht_storage.as_mut().unwrap().sync()?; + Self::settle_and_sync(&mut f).await?; log::info!("SIMULATION END"); state.success(); @@ -139,6 +130,7 @@ impl SimulationDhtTarget { enable_sync: bool, timeout_ms: u32, experiment_id: u32, + evil_noforward: bool, ) -> anyhow::Result<()> { let start_instant = Instant::now(); @@ -195,6 +187,16 @@ impl SimulationDhtTarget { if iteration % 10 == 0 { let response = state.update_and_upload(&mut f).await; page_id_opt = response.target_page_id; + + if page_id_opt.is_some() { + // this node shall become malicious now if the flag is set + // because the target page was propagated + log::info!("(re)becoming malicious node"); + unsafe { + flmodules::dht_storage::messages::EVIL_NO_FORWARD = evil_noforward; + flmodules::dht_router::messages::EVIL_NO_FORWARD = evil_noforward; + } + } } // Stop here each iteration @@ -204,10 +206,6 @@ impl SimulationDhtTarget { continue; } - // todo!("test this"); - //continue; // Testing whether pages propagate with no - // get_flo - let page_id = FloID::from_str(&page_id_opt.clone().unwrap())?; let page_global_id = GlobalID::new(realm_id.clone(), page_id.clone()); diff --git a/cli/fledger/src/state.rs b/cli/fledger/src/state.rs index ead5c53d..57f6542c 100644 --- a/cli/fledger/src/state.rs +++ b/cli/fledger/src/state.rs @@ -9,6 +9,12 @@ use flmodules::dht_storage::messages::DsMetrics; use flmodules::flo::blob::{BlobAccess, BlobPage}; use std::any::type_name; +#[derive(Clone, Debug, Default)] +pub struct Page { + pub name: String, + pub id: String, +} + #[derive(Clone, Debug, Default)] pub struct SimulationState { pub experiment_id: u32, @@ -17,6 +23,7 @@ pub struct SimulationState { pub node_status: String, pub pages_stored: Vec, + pub pages_created: Vec, pub connected_nodes_total: u32, pub ds_size_bytes: u64, pub evil_no_forward: bool, @@ -92,6 +99,7 @@ impl SimulationState { self.ds_metrics = ds.stats.borrow().experiment_stats.clone(); self.connected_nodes_total = connected_nodes_total; self.ds_size_bytes = ds_size; + self.evil_no_forward = f.args.evil_noforward.clone(); self.refresh_pages(ds).await; self.upload() diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index dc06e53f..3bd425b4 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -1,4 +1,4 @@ -use std::{any::type_name, collections::HashMap}; +use std::collections::HashMap; use flarch::{ broker::SubsystemHandler, @@ -90,11 +90,11 @@ pub struct RealmStats { #[derive(Serialize, Debug, Default, Clone)] pub struct DsMetrics { pub store_flo_total: u32, + pub request_flo_metas_sent_total: u32, // TODO: what? pub flo_value_sent_total: u32, pub flo_value_sent_blocked_total: u32, pub available_flos_sent_total: u32, pub available_flos_sent_blocked_total: u32, - pub request_flos_sent_total: u32, pub flos_sent_total: u32, pub flos_sent_blocked_total: u32, pub max_flo_metas_received_in_available_flos: u32, @@ -350,6 +350,7 @@ impl Messages { self.realms.keys().cloned().collect(), )], MessageNeighbour::AvailableRealmIDs(realm_ids) => { + increment_stat!(self, self.experiment_stats.request_flo_metas_sent_total); let accepted_realms = realm_ids .into_iter() .filter(|rid| self.config.accepts_realm(&rid)) @@ -414,21 +415,10 @@ impl Messages { self.realms .get(&realm_id) .map(|realm| { - let flos = flo_ids + flo_ids .iter() .filter_map(|id| realm.get_flo_cuckoo(id)) - .collect::>(); - - let realm = flos - .iter() - .find(|flo| flo.0.flo_type() == type_name::()); - - let flo_to_forward = realm.or(flos.last()); - if let Some(flo) = flo_to_forward { - return vec![flo.clone()]; - } else { - return vec![]; - } + .collect::>() }) .map_or(vec![], |flos| { //log::info!("Flos to send: {}", flos.len()); From 989e833143885382d1448fe174d7e08414d9c2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Fri, 13 Jun 2025 22:51:48 +0200 Subject: [PATCH 39/43] feat: variable target amount --- cli/fledger/src/hermes/api.rs | 17 +- cli/fledger/src/hermes/snapshot.rs | 16 +- cli/fledger/src/hermes/update_response.rs | 4 +- cli/fledger/src/influx/lines.rs | 7 - cli/fledger/src/main.rs | 2 +- cli/fledger/src/simulation.rs | 20 +- .../mod.rs | 0 .../simulation.rs | 175 +++++++++--------- cli/fledger/src/state.rs | 36 ++-- 9 files changed, 141 insertions(+), 136 deletions(-) rename cli/fledger/src/{simulation_dht_target => simulation_dht}/mod.rs (100%) rename cli/fledger/src/{simulation_dht_target => simulation_dht}/simulation.rs (55%) diff --git a/cli/fledger/src/hermes/api.rs b/cli/fledger/src/hermes/api.rs index 33487f4b..fd1f8a8d 100644 --- a/cli/fledger/src/hermes/api.rs +++ b/cli/fledger/src/hermes/api.rs @@ -1,6 +1,6 @@ use crate::hermes::snapshot::Snapshot; use crate::hermes::update_response::UpdateResponse; -use crate::state::SimulationState; +use crate::state::{Page, SimulationState}; use anyhow::Error; use reqwest::Method; use serde::Serialize; @@ -45,6 +45,21 @@ impl HermesApi { } } + pub fn store_target_pages( + &self, + experiment_id: u32, + target_pages: Vec, + ) -> Result<(), Error> { + let mut data = HashMap::new(); + data.insert("target_pages", target_pages); + let url = format!( + "https://fledger.yohan.ch/api/experiments/{}/store-target-pages", + experiment_id + ); + self.api_request(Method::POST, url, &data)?; + Ok(()) + } + fn api_request( &self, method: Method, diff --git a/cli/fledger/src/hermes/snapshot.rs b/cli/fledger/src/hermes/snapshot.rs index cbeeab91..c5037168 100644 --- a/cli/fledger/src/hermes/snapshot.rs +++ b/cli/fledger/src/hermes/snapshot.rs @@ -1,12 +1,11 @@ -use crate::state::SimulationState; +use crate::state::{Page, SimulationState}; use serde::Serialize; #[derive(Serialize, Default, Debug, Clone)] pub struct Snapshot { node_status: String, - pages_stored: Vec, + pages_stored: Vec, evil_no_forward: bool, - target_page_id: Option, timed_metrics: Vec<(String, u32)>, timeless_metrics: Vec<(String, u32)>, @@ -118,18 +117,15 @@ impl Snapshot { simulation_state.ds_metrics.max_flos_received_in_flos, ); - if let Some(target_page_stored_bool) = simulation_state.target_page_stored_bool { - timeless_metrics.add_metric( - "target_page_stored_bool".to_string(), - target_page_stored_bool as u32, - ); - } + timeless_metrics.add_metric( + "target_successfully_fetched_total".to_string(), + simulation_state.target_successfully_fetched_total, + ); Snapshot { node_status: simulation_state.node_status, pages_stored: simulation_state.pages_stored, evil_no_forward: simulation_state.evil_no_forward, - target_page_id: simulation_state.target_page_id, timed_metrics: timed_metrics.build(), timeless_metrics: timeless_metrics.build(), diff --git a/cli/fledger/src/hermes/update_response.rs b/cli/fledger/src/hermes/update_response.rs index 75799315..a78d017c 100644 --- a/cli/fledger/src/hermes/update_response.rs +++ b/cli/fledger/src/hermes/update_response.rs @@ -2,9 +2,7 @@ use serde::Deserialize; #[derive(Deserialize, Debug, Clone, Default)] pub struct UpdateResponse { - pub target_page_id: Option, - pub timed_data_points: Vec, - pub timeless_data_points: Vec, + pub target_page_ids: Vec, } impl UpdateResponse {} diff --git a/cli/fledger/src/influx/lines.rs b/cli/fledger/src/influx/lines.rs index f9016df3..39d6756c 100644 --- a/cli/fledger/src/influx/lines.rs +++ b/cli/fledger/src/influx/lines.rs @@ -83,13 +83,6 @@ impl InfluxLines { stats.ds_metrics.store_flo_total, )); - if let Some(target_page_stored_bool) = stats.target_page_stored_bool { - lines.push(generator.create_influx_line( - "fledger_simulation_dht_target_state".to_string(), - target_page_stored_bool as u32, - )); - } - lines.join("\n") } } diff --git a/cli/fledger/src/main.rs b/cli/fledger/src/main.rs index 4a764fd1..2495f91e 100644 --- a/cli/fledger/src/main.rs +++ b/cli/fledger/src/main.rs @@ -21,7 +21,7 @@ mod page; mod realm; mod simulation; mod simulation_chat; -mod simulation_dht_target; +mod simulation_dht; mod simulation_realm; mod state; diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index d3e7ce07..914cd705 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -1,6 +1,7 @@ use crate::simulation_chat::simulation::SimulationChat; +use crate::simulation_dht::simulation::SimulationDht; use crate::simulation_realm::simulation::SimulationRealm; -use crate::{simulation_dht_target::simulation::SimulationDhtTarget, Fledger}; +use crate::Fledger; use clap::{arg, Args, Subcommand}; use flarch::random; use flarch::tasks::wait_ms; @@ -30,10 +31,13 @@ pub enum SimulationSubcommand { DhtJoinRealm {}, - DhtCreateFillersAndTarget { + DhtCreatePages { #[arg(long)] filler_amount: u32, + #[arg(long)] + target_amount: u32, + #[arg(long)] page_size: u32, @@ -47,7 +51,7 @@ pub enum SimulationSubcommand { experiment_id: u32, }, - DhtFetchTarget { + DhtFetchPages { #[arg(long, default_value = "20000")] timeout_ms: u32, @@ -77,16 +81,18 @@ impl SimulationHandler { SimulationChat::run_chat(f, command, send_msg, recv_msg).await } SimulationSubcommand::DhtJoinRealm {} => SimulationRealm::run_dht_join_realm(f).await, - SimulationSubcommand::DhtCreateFillersAndTarget { + SimulationSubcommand::DhtCreatePages { filler_amount, + target_amount, page_size, pages_propagation_delay, connection_delay, experiment_id, } => { - SimulationDhtTarget::run_create_fillers_and_target( + SimulationDht::run_create_pages( f, filler_amount, + target_amount, page_size, pages_propagation_delay, connection_delay, @@ -94,13 +100,13 @@ impl SimulationHandler { ) .await } - SimulationSubcommand::DhtFetchTarget { + SimulationSubcommand::DhtFetchPages { timeout_ms, enable_sync, experiment_id, } => { let evil_noforward = f.args.evil_noforward.clone(); - SimulationDhtTarget::fetch_target( + SimulationDht::run_fetch_pages( f, loop_delay, enable_sync, diff --git a/cli/fledger/src/simulation_dht_target/mod.rs b/cli/fledger/src/simulation_dht/mod.rs similarity index 100% rename from cli/fledger/src/simulation_dht_target/mod.rs rename to cli/fledger/src/simulation_dht/mod.rs diff --git a/cli/fledger/src/simulation_dht_target/simulation.rs b/cli/fledger/src/simulation_dht/simulation.rs similarity index 55% rename from cli/fledger/src/simulation_dht_target/simulation.rs rename to cli/fledger/src/simulation_dht/simulation.rs index 746dbe0f..41a93bef 100644 --- a/cli/fledger/src/simulation_dht_target/simulation.rs +++ b/cli/fledger/src/simulation_dht/simulation.rs @@ -1,6 +1,9 @@ -use anyhow::Error; +use crate::hermes::api::HermesApi; +use crate::state::{Page, SimulationState}; +use crate::Fledger; use flarch::tasks::{time::timeout, wait_ms}; use flcrypto::tofrombytes::ToFromBytes; +use flmodules::flo::realm::RealmID; use flmodules::{ dht_storage::realm_view::RealmView, flo::{ @@ -9,20 +12,24 @@ use flmodules::{ realm::GlobalID, }, }; +use std::collections::HashSet; use std::str::FromStr; use std::time::{Duration, Instant}; -use crate::state::SimulationState; -use crate::Fledger; - #[derive(Clone)] -pub struct SimulationDhtTarget {} +pub struct SimulationDht {} + +impl SimulationDht { + fn get_page_content(page: &FloWrapper) -> String { + String::from_utf8(page.datas().iter().next().unwrap().1.clone().to_vec()).unwrap() + } + + fn get_page_name(page: &FloWrapper) -> String { + page.values().iter().next().unwrap().1.clone() + } -impl SimulationDhtTarget { fn log_page_info(flo_page: &FloWrapper) { - let page_content = - String::from_utf8(flo_page.datas().iter().next().unwrap().1.clone().to_vec()) - .unwrap_or_default(); + let page_content = Self::get_page_content(flo_page); log::info!( "page {}/{}/{} | {} | {} ({}B -> {}B)", flo_page.flo_id(), @@ -60,16 +67,23 @@ impl SimulationDhtTarget { Ok(()) } - pub async fn run_create_fillers_and_target( + fn make_page_id(realm_id: RealmID, page_id: FloID) -> GlobalID { + GlobalID::new(realm_id.clone(), page_id.clone()) + } + + async fn fetch_page(f: &mut Fledger, id: GlobalID) -> anyhow::Result> { + f.node.dht_storage.as_mut().unwrap().get_flo(&id).await + } + + pub async fn run_create_pages( mut f: Fledger, filler_amount: u32, + target_amount: u32, page_size: u32, - pages_propagation_delay: u32, + propagation_delay: u32, connection_delay: u32, experiment_id: u32, ) -> anyhow::Result<()> { - let mut state = SimulationState::new(experiment_id, f.node.node_config.info.name.clone()); - f.loop_node(crate::FledgerState::DHTAvailable).await?; log::info!("DHT CONNECTED"); @@ -85,46 +99,48 @@ impl SimulationDhtTarget { wait_ms(500).await; Self::create_flo_page( &mut rv, - &format!("simulation-filler-{}", i.to_string()), + &format!("filler-{}", i.to_string()), String::from_utf8(vec![b'-'; page_size as usize])?, ) .await?; } - log::info!("[Waiting for fillers to settle]"); - log::info!("{} ms", pages_propagation_delay); + log::info!("[Create target_pages simulation flo page]"); + let mut target_pages = vec![]; + for i in 0..target_amount { + wait_ms(500).await; + target_pages.push( + Self::create_flo_page( + &mut rv, + &format!("target-{}", i.to_string()), + String::from_utf8(vec![b'o'; page_size as usize])?, + ) + .await?, + ); + } + log::info!("[Waiting for pages to settle]"); + log::info!("{} ms", propagation_delay); Self::settle_and_sync(&mut f).await?; - wait_ms(pages_propagation_delay as u64).await; + wait_ms(propagation_delay as u64).await; - log::info!("[Sending simulation flo page]"); - let flo_page = Self::create_flo_page( - &mut rv, - "simulation-page", - String::from_utf8(vec![b'o'; page_size as usize])?, - ) - .await?; - - log::info!("[Waiting for target page to propagate]"); - log::info!("5000 ms"); - wait_ms(5000).await; + let pages = target_pages + .iter() + .map(|page| Page { + id: page.flo_id().to_string(), + name: Self::get_page_name(page), + }) + .collect::>(); - state.target_page_id = Some(flo_page.flo_id().to_string()); - state.update_and_upload(&mut f).await; - - wait_ms(1000).await; - - Self::settle_and_sync(&mut f).await?; + HermesApi::default().store_target_pages(experiment_id, pages)?; log::info!("SIMULATION END"); - state.success(); - state.update_and_upload(&mut f).await; f.loop_node(crate::FledgerState::Forever).await?; Ok(()) } - pub async fn fetch_target( + pub async fn run_fetch_pages( mut f: Fledger, loop_delay: u32, enable_sync: bool, @@ -157,10 +173,10 @@ impl SimulationDhtTarget { .realm .realm_id(); - let mut iteration = 0u32; - let mut page_id_opt: Option = None; + let mut target_page_ids = HashSet::new(); + let mut fetched_page_ids = HashSet::new(); - // Loop until page_id found + let mut iteration = 0u32; loop { if start_instant.elapsed().as_millis() > timeout_ms as u128 { @@ -179,71 +195,50 @@ impl SimulationDhtTarget { if enable_sync { f.node.dht_storage.as_mut().unwrap().sync()?; } - // let _ = rv - // .update_all() - // .await - // .inspect_err(|e| log::error!("error when doing rv.update_all(): {e}")); - if iteration % 10 == 0 { + if iteration % 30 == 0 { let response = state.update_and_upload(&mut f).await; - page_id_opt = response.target_page_id; + target_page_ids.clear(); + for target_page_id in response.target_page_ids { + target_page_ids.insert(target_page_id.clone()); + } - if page_id_opt.is_some() { - // this node shall become malicious now if the flag is set - // because the target page was propagated - log::info!("(re)becoming malicious node"); + if !target_page_ids.is_empty() && evil_noforward { unsafe { - flmodules::dht_storage::messages::EVIL_NO_FORWARD = evil_noforward; - flmodules::dht_router::messages::EVIL_NO_FORWARD = evil_noforward; + if evil_noforward && !flmodules::dht_storage::messages::EVIL_NO_FORWARD { + log::info!("becoming a malicious node"); + log::info!("SIMULATION END"); + flmodules::dht_storage::messages::EVIL_NO_FORWARD = true; + flmodules::dht_router::messages::EVIL_NO_FORWARD = true; + } } } } - // Stop here each iteration - // until page id is known - if page_id_opt.is_none() { - log::info!("failed to get page id"); - continue; + for page_id in target_page_ids.clone() { + if fetched_page_ids.contains(&page_id.clone()) { + continue; + } + let flo_id = FloID::from_str(&page_id.clone())?; + let global_page_id = Self::make_page_id(realm_id.clone(), flo_id); + let page = Self::fetch_page(&mut f, global_page_id).await; + if page.is_ok() { + fetched_page_ids.insert(page_id.clone()); + } } - let page_id = FloID::from_str(&page_id_opt.clone().unwrap())?; - - let page_global_id = GlobalID::new(realm_id.clone(), page_id.clone()); - let page_flo_wrapper_result: Result, Error> = f - .node - .dht_storage - .as_mut() - .unwrap() - .get_flo(&page_global_id) - .await; - - if let Ok(page_flo_wrapper) = page_flo_wrapper_result { - let page_flo = page_flo_wrapper.flo(); - let page_blob = - BlobPage::from_rmp_bytes(page_flo.flo_type().as_str(), page_flo.data())?; - let page_content = String::from_utf8( - page_blob - .0 - .datas() - .iter() - .next() - .unwrap() - .1 - .clone() - .to_vec(), - ) - .unwrap_or_default(); - log::info!( - "simulation page found with content: {}", - page_content.chars().take(50).collect::() - ); + // log fetched and target pages + log::info!("fetched pages: {fetched_page_ids:?}"); + log::info!("target pages: {target_page_ids:?}"); + state.target_successfully_fetched_total = fetched_page_ids.len() as u32; + + if !target_page_ids.is_empty() && fetched_page_ids.is_superset(&target_page_ids) { + log::info!("all target pages fetched."); log::info!("SIMULATION END"); state.success(); state.update_and_upload(&mut f).await; f.loop_node(crate::FledgerState::Forever).await?; - - return Ok(()); } } } diff --git a/cli/fledger/src/state.rs b/cli/fledger/src/state.rs index 57f6542c..78d51d02 100644 --- a/cli/fledger/src/state.rs +++ b/cli/fledger/src/state.rs @@ -7,9 +7,10 @@ use flcrypto::tofrombytes::ToFromBytes; use flmodules::dht_storage::broker::DHTStorage; use flmodules::dht_storage::messages::DsMetrics; use flmodules::flo::blob::{BlobAccess, BlobPage}; +use serde::{Deserialize, Serialize}; use std::any::type_name; -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct Page { pub name: String, pub id: String, @@ -22,18 +23,14 @@ pub struct SimulationState { pub node_name: String, pub node_status: String, - pub pages_stored: Vec, - pub pages_created: Vec, + pub pages_stored: Vec, pub connected_nodes_total: u32, pub ds_size_bytes: u64, pub evil_no_forward: bool, + pub target_successfully_fetched_total: u32, pub ds_metrics: DsMetrics, - // simulation_dht_target specific fields - pub target_page_stored_bool: Option, - pub target_page_id: Option, - pub api: HermesApi, pub influx: InfluxApi, } @@ -61,19 +58,24 @@ impl SimulationState { }) .iter() .filter(|flo| flo.flo_type() == type_name::()) - .map(|flo| BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap()) - .map(|page| page.0.values().iter().next().unwrap().1.clone()) - .map(|name| { - return name.replace("simulation-filler-", ""); + .map(|flo| { + let page = BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap(); + Page { + id: flo.flo_id().to_string(), + name: page.0.values().iter().next().unwrap().1.clone(), + } }) - .collect::>(); - - if pages_stored.contains(&"simulation-page".to_string()) { - self.target_page_stored_bool = Some(true); - } + .collect::>(); self.pages_stored = pages_stored.clone(); - log::info!("pages stored: {}", pages_stored.join(", ")); + log::info!( + "pages stored: {}", + pages_stored + .iter() + .map(|page| page.name.clone()) + .collect::>() + .join(", ") + ); } pub fn success(&mut self) { From 80666379e718965d8b9c2529a72e66aec7111d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Wed, 2 Jul 2025 23:31:00 +0200 Subject: [PATCH 40/43] feat: tweaking timeouts --- cli/fledger/src/hermes/api.rs | 30 +++++++- cli/fledger/src/simulation.rs | 7 +- cli/fledger/src/simulation_dht/simulation.rs | 79 +++++++++++++++++--- 3 files changed, 104 insertions(+), 12 deletions(-) diff --git a/cli/fledger/src/hermes/api.rs b/cli/fledger/src/hermes/api.rs index fd1f8a8d..82017294 100644 --- a/cli/fledger/src/hermes/api.rs +++ b/cli/fledger/src/hermes/api.rs @@ -3,7 +3,7 @@ use crate::hermes::update_response::UpdateResponse; use crate::state::{Page, SimulationState}; use anyhow::Error; use reqwest::Method; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; use tokio::time::Instant; @@ -18,6 +18,11 @@ pub struct HermesApi { client: reqwest::blocking::Client, } +#[derive(Deserialize, Debug, Clone, Default)] +pub struct LostTargetPagesResponse { + pub lost_target_pages: Vec, +} + impl HermesApi { pub fn update(&self, state: SimulationState) -> Result { let snapshot = Snapshot::make(state.clone()); @@ -45,6 +50,29 @@ impl HermesApi { } } + pub fn get_lost_target_pages( + &self, + experiment_id: u32, + ) -> Result { + let url = format!( + "https://fledger.yohan.ch/api/experiments/{}/lost-target-pages", + experiment_id, + ); + let response = self.api_request(Method::GET, url, &())?; + let lost_target_pages: LostTargetPagesResponse = + serde_json::from_str(&response).map_err(|e| Error::new(e))?; + Ok(lost_target_pages) + } + + pub fn start_fetching(&self, experiment_id: u32) -> Result<(), Error> { + let url = format!( + "https://fledger.yohan.ch/api/experiments/{}/start-fetching", + experiment_id + ); + self.api_request(Method::GET, url, &())?; + Ok(()) + } + pub fn store_target_pages( &self, experiment_id: u32, diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index 914cd705..fc5751e9 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -52,7 +52,10 @@ pub enum SimulationSubcommand { }, DhtFetchPages { - #[arg(long, default_value = "20000")] + #[arg(long, default_value = "1200000")] + propagation_timeout_ms: u32, + + #[arg(long, default_value = "600000")] timeout_ms: u32, #[arg(long, default_value = "false")] @@ -101,6 +104,7 @@ impl SimulationHandler { .await } SimulationSubcommand::DhtFetchPages { + propagation_timeout_ms, timeout_ms, enable_sync, experiment_id, @@ -110,6 +114,7 @@ impl SimulationHandler { f, loop_delay, enable_sync, + propagation_timeout_ms, timeout_ms, experiment_id, evil_noforward, diff --git a/cli/fledger/src/simulation_dht/simulation.rs b/cli/fledger/src/simulation_dht/simulation.rs index 41a93bef..43938b04 100644 --- a/cli/fledger/src/simulation_dht/simulation.rs +++ b/cli/fledger/src/simulation_dht/simulation.rs @@ -105,10 +105,15 @@ impl SimulationDht { .await?; } + log::info!("[Waiting for fillers to settle]"); + log::info!("{} ms", propagation_delay); + Self::settle_and_sync(&mut f).await?; + wait_ms(propagation_delay as u64).await; + log::info!("[Create target_pages simulation flo page]"); let mut target_pages = vec![]; for i in 0..target_amount { - wait_ms(500).await; + wait_ms(3000).await; target_pages.push( Self::create_flo_page( &mut rv, @@ -119,7 +124,7 @@ impl SimulationDht { ); } - log::info!("[Waiting for pages to settle]"); + log::info!("[Waiting for targets to settle]"); log::info!("{} ms", propagation_delay); Self::settle_and_sync(&mut f).await?; wait_ms(propagation_delay as u64).await; @@ -132,8 +137,41 @@ impl SimulationDht { }) .collect::>(); - HermesApi::default().store_target_pages(experiment_id, pages)?; + let hermes = HermesApi::default(); + hermes.store_target_pages(experiment_id, pages)?; + + let mut all_targets_propagated = false; + let start_instant = Instant::now(); + while !all_targets_propagated { + let lost_target_pages = hermes.get_lost_target_pages(experiment_id)?; + if start_instant.elapsed() > Duration::from_secs(60 * 15) { + log::warn!("PROPAGATION TIMEOUT REACHED (15min)"); + log::info!("SIMULATION END"); + return Err(anyhow::anyhow!("timeout")); + } + + if lost_target_pages.lost_target_pages.is_empty() { + all_targets_propagated = true; + } else { + let pages_to_repropagate = target_pages + .iter() + .filter(|page| { + lost_target_pages + .lost_target_pages + .contains(&page.flo_id().to_string().clone()) + }) + .collect::>>(); + for page in pages_to_repropagate { + log::info!("REPROPAGATE: {}", Self::get_page_name(page)); + f.ds.store_flo(page.flo().clone())?; + wait_ms(500).await + } + wait_ms(30000).await; + } + } + + hermes.start_fetching(experiment_id)?; log::info!("SIMULATION END"); f.loop_node(crate::FledgerState::Forever).await?; @@ -144,11 +182,12 @@ impl SimulationDht { mut f: Fledger, loop_delay: u32, enable_sync: bool, + propagation_timeout_ms: u32, timeout_ms: u32, experiment_id: u32, evil_noforward: bool, ) -> anyhow::Result<()> { - let start_instant = Instant::now(); + let mut start_instant = Instant::now(); let node_name = f.node.node_config.info.name.clone(); let mut state = SimulationState::new(experiment_id, node_name); @@ -179,7 +218,21 @@ impl SimulationDht { let mut iteration = 0u32; loop { - if start_instant.elapsed().as_millis() > timeout_ms as u128 { + if target_page_ids.is_empty() + && start_instant.elapsed().as_millis() > propagation_timeout_ms as u128 + { + log::warn!("PROPAGATION TIMEOUT REACHED ({}ms)", propagation_timeout_ms); + log::info!("SIMULATION END"); + state.timeout(); + state.update_and_upload(&mut f).await; + f.loop_node(crate::FledgerState::Forever).await?; + + return Ok(()); + } + + if !target_page_ids.is_empty() + && start_instant.elapsed().as_millis() > timeout_ms as u128 + { log::warn!("SIMULATION TIMEOUT REACHED ({}ms)", timeout_ms); log::info!("SIMULATION END"); state.timeout(); @@ -198,12 +251,18 @@ impl SimulationDht { if iteration % 30 == 0 { let response = state.update_and_upload(&mut f).await; - target_page_ids.clear(); - for target_page_id in response.target_page_ids { - target_page_ids.insert(target_page_id.clone()); - } + if target_page_ids.is_empty() && !response.target_page_ids.is_empty() { + // target propagation ended, it's now time to fetch. + // resetting the timeout + log::info!( + "TIMEOUT RESET AFTER {}s, STARTING TO FETCH PAGES", + start_instant.elapsed().as_secs() + ); + start_instant = Instant::now(); + for target_page_id in response.target_page_ids { + target_page_ids.insert(target_page_id.clone()); + } - if !target_page_ids.is_empty() && evil_noforward { unsafe { if evil_noforward && !flmodules::dht_storage::messages::EVIL_NO_FORWARD { log::info!("becoming a malicious node"); From 45fecc78a0e9e2148922997df380da2d2c8d8117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Thu, 3 Jul 2025 23:17:55 +0200 Subject: [PATCH 41/43] feat: local blacklists --- Makefile | 11 ++ cli/fledger/src/realm.rs | 1 + cli/fledger/src/simulation.rs | 8 ++ cli/fledger/src/simulation_dht/simulation.rs | 39 +++++ flmodules/src/dht_router/messages.rs | 143 +++++++++++++++++-- flmodules/src/dht_storage/messages.rs | 19 +-- 6 files changed, 205 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 08217b6b..9eb67da2 100644 --- a/Makefile +++ b/Makefile @@ -129,6 +129,17 @@ deploy: cd cli && cargo build --release --target=x86_64-unknown-linux-musl -p fledger && cargo build --release --target=x86_64-unknown-linux-musl -p flsignal @./deploy-binaries.sh +test_realm: + /usr/bin/mktemp -d -t fledger-XXXX + ./target-common/x86_64-unknown-linux-musl/release/fledger --config "$(shell /usr/bin/mktemp -d -t fledger-XXXX)" --disable-turn-stun --signal-url ws://localhost:8765 realm create simulation --cond-pass + +test_signal: + ./target-common/x86_64-unknown-linux-musl/release/flsignal -v --max-list-len 25 + +test_just_fetch_once: + rm -rf /tmp/fledger + ./target-common/x86_64-unknown-linux-musl/release/fledger --config "$(shell /usr/bin/mktemp -d -t fledger-XXXX)" --disable-turn-stun --signal-url ws://localhost:8765 simulation just-fetch-once + clean: for c in ${CARGOS}; do \ echo "Cleaning $$c"; \ diff --git a/cli/fledger/src/realm.rs b/cli/fledger/src/realm.rs index a9460dc4..9e46495d 100644 --- a/cli/fledger/src/realm.rs +++ b/cli/fledger/src/realm.rs @@ -49,6 +49,7 @@ impl RealmHandler { cond_pass: bool, ) -> anyhow::Result<()> { f.loop_node(crate::FledgerState::Connected(1)).await?; + //f.loop_node(crate::FledgerState::Duration(1)).await?; let config = RealmConfig { max_space: max_space.unwrap_or(1000000), diff --git a/cli/fledger/src/simulation.rs b/cli/fledger/src/simulation.rs index fc5751e9..a907f4bc 100644 --- a/cli/fledger/src/simulation.rs +++ b/cli/fledger/src/simulation.rs @@ -63,7 +63,12 @@ pub enum SimulationSubcommand { #[arg(long)] experiment_id: u32, + + #[arg(long, default_value = "false")] + with_local_blacklists: bool, }, + + JustFetchOnce {}, } pub struct SimulationHandler {} @@ -108,6 +113,7 @@ impl SimulationHandler { timeout_ms, enable_sync, experiment_id, + with_local_blacklists, } => { let evil_noforward = f.args.evil_noforward.clone(); SimulationDht::run_fetch_pages( @@ -118,9 +124,11 @@ impl SimulationHandler { timeout_ms, experiment_id, evil_noforward, + with_local_blacklists, ) .await } + SimulationSubcommand::JustFetchOnce {} => SimulationDht::just_fetch_once(f).await, } } } diff --git a/cli/fledger/src/simulation_dht/simulation.rs b/cli/fledger/src/simulation_dht/simulation.rs index 43938b04..fdb721b3 100644 --- a/cli/fledger/src/simulation_dht/simulation.rs +++ b/cli/fledger/src/simulation_dht/simulation.rs @@ -186,9 +186,18 @@ impl SimulationDht { timeout_ms: u32, experiment_id: u32, evil_noforward: bool, + with_local_blacklists: bool, ) -> anyhow::Result<()> { let mut start_instant = Instant::now(); + unsafe { + if with_local_blacklists { + log::info!("enabling local blacklists"); + flmodules::dht_storage::messages::LOCAL_BLACKLISTS = true; + flmodules::dht_router::messages::LOCAL_BLACKLISTS = true; + } + } + let node_name = f.node.node_config.info.name.clone(); let mut state = SimulationState::new(experiment_id, node_name); @@ -301,4 +310,34 @@ impl SimulationDht { } } } + + pub async fn just_fetch_once(mut f: Fledger) -> anyhow::Result<()> { + f.loop_node(crate::FledgerState::DHTAvailable).await?; + f.loop_node(crate::FledgerState::Connected(2)).await?; + + let realm_id = RealmView::new_first(f.node.dht_storage.as_ref().unwrap().clone()) + .await? + .realm + .realm_id(); + + let flo_id = + FloID::from_str("5efe0a6143df5641af9d6036ba8da82222bb30211c21ba5ec236851efda38420")?; + let global_page_id = Self::make_page_id(realm_id.clone(), flo_id); + + for i in 0..10 { + wait_ms(1000).await; + + let page = Self::fetch_page(&mut f, global_page_id.clone()).await; + + if page.is_ok() { + log::info!("God made a miracle"); + } else { + log::info!("Fetch {i} done."); + } + } + + f.loop_node(crate::FledgerState::Forever).await?; + + Ok(()) + } } diff --git a/flmodules/src/dht_router/messages.rs b/flmodules/src/dht_router/messages.rs index 5f78c4e8..63f1ab38 100644 --- a/flmodules/src/dht_router/messages.rs +++ b/flmodules/src/dht_router/messages.rs @@ -1,13 +1,17 @@ +use std::collections::{HashMap, HashSet}; + use flarch::{ broker::{SubsystemHandler, TranslateFrom, TranslateInto}, nodeids::{NodeID, U256}, platform_async_trait, }; -use rand::seq::SliceRandom; +use itertools::Itertools; +use rand::{seq::SliceRandom, Rng}; use serde::{Deserialize, Serialize}; use tokio::sync::watch; use crate::{ + dht_storage::messages::MessageClosest, nodeconfig::NodeInfo, router::messages::{NetworkWrapper, RouterIn, RouterOut}, timer::TimerMessage, @@ -19,6 +23,7 @@ use super::{ }; pub static mut EVIL_NO_FORWARD: bool = false; +pub static mut LOCAL_BLACKLISTS: bool = false; /// These are the messages which will be exchanged between the nodes for this /// module. @@ -73,6 +78,11 @@ pub(super) struct Messages { // This is different than core.active, because there can be connections from other // modules, or connections from another node. connected: Vec, + + // readFlo id -> node id + requests_in_flight: HashMap, + + blacklisted_nodes: HashSet, } impl Messages { @@ -84,6 +94,8 @@ impl Messages { core: Kademlia::new(root, cfg), tx: Some(tx), connected: vec![], + requests_in_flight: HashMap::new(), + blacklisted_nodes: HashSet::new(), }, rx, ) @@ -233,22 +245,137 @@ impl Messages { } } + fn get_readflo_id(&self, msg: NetworkWrapper) -> Option { + if msg.module == "DHTStorage" { + match msg.unwrap_yaml("DHTStorage") { + Some(msg_readflo) => match msg_readflo { + MessageClosest::ReadFlo(_id, request_id) => { + log::warn!("sending MessageClosest::ReadFlo {}", msg.msg.clone()); + Some(request_id.clone()) + } + _ => None, + }, + None => None, + } + } else { + None + } + } + + fn blacklist_bad_nodes(&mut self) { + unsafe { + if !self::LOCAL_BLACKLISTS { + return; + } + } + + let in_flight = self.requests_in_flight.clone(); + in_flight + .iter() + .map(|(_request_id, node_id)| node_id) + .counts() + .iter() + .for_each(|(node_id_request, count)| { + let node_id1 = (*node_id_request).clone(); + if count.clone() > 10 { + // made 10 requests to this node + // remove the node from requests_in_flight to reset the counter + // and blacklist the node + log::warn!("blacklisting {node_id1}."); + let request_ids = self + .requests_in_flight + .clone() + .iter() + .filter(|(_request_id, node_id2)| node_id1 == **node_id2) + .map(|(request_id, _node_id)| request_id) + .copied() + .collect_vec(); + for value in request_ids { + self.requests_in_flight.remove_entry(&value); + } + self.core.remove_node(&node_id1); + self.blacklisted_nodes.insert(node_id1); + } + }); + } + + fn randomly_whitelist(&mut self) { + unsafe { + if !self::LOCAL_BLACKLISTS { + return; + } + } + + let mut rng = rand::thread_rng(); + if !self.blacklisted_nodes.is_empty() && rng.gen_bool(0.05) { + let len = self.blacklisted_nodes.len(); + if len > 0 { + let random_index = rng.gen_range(0..len); + let node_id_opt = self.blacklisted_nodes.iter().nth(random_index).clone(); + if let Some(node_id) = node_id_opt { + log::warn!("whitelisting {node_id}."); + self.core.add_node(node_id.clone()); + self.blacklisted_nodes.remove(&(node_id.clone())); + } + } + } + } + + fn log_requests_in_flight(&self) { + unsafe { + if !self::LOCAL_BLACKLISTS { + return; + } + } + + log::info!("counts:"); + self.requests_in_flight + .iter() + .map(|tuple| tuple.1) + .counts() + .iter() + .for_each(|item| log::info!(" {} -> {}", item.0, item.1)) + } + fn message_closest( - &self, + &mut self, orig: NodeID, last_hop: NodeID, key: U256, msg: NetworkWrapper, ) -> Vec { - match self + let readflo_id_opt = self.get_readflo_id(msg.clone()); + + self.blacklist_bad_nodes(); + + let closest = self .closest_or_connected(key.clone(), Some(&last_hop)) .first() - { - Some(&next_hop) => vec![ - ModuleMessage::Closest(orig, key, msg.clone()).wrapper_network(next_hop), - DHTRouterOut::MessageRouting(orig, last_hop, next_hop, key, msg).into(), - ], + .copied(); + + self.randomly_whitelist(); + + match closest.clone() { + Some(next_hop) => { + if self.blacklisted_nodes.contains(&next_hop) { + log::error!("IMPOSSIBLE?: sending a message to a blacklisted node."); + } + + if let Some(readflo_id) = readflo_id_opt { + log::info!("NEXT HOP: {}", next_hop); + self.requests_in_flight + .insert(readflo_id.clone(), next_hop.clone()); + self.log_requests_in_flight(); + } + vec![ + ModuleMessage::Closest(orig, key, msg.clone()).wrapper_network(next_hop), + DHTRouterOut::MessageRouting(orig, last_hop, next_hop, key, msg).into(), + ] + } None => { + if readflo_id_opt.is_some() { + log::warn!("NO NEXT HOP!"); + } if key == self.core.root { vec![DHTRouterOut::MessageDest(orig, last_hop, msg).into()] } else { diff --git a/flmodules/src/dht_storage/messages.rs b/flmodules/src/dht_storage/messages.rs index 3bd425b4..2dfba8a9 100644 --- a/flmodules/src/dht_storage/messages.rs +++ b/flmodules/src/dht_storage/messages.rs @@ -47,7 +47,8 @@ pub enum MessageClosest { // which have enough place left. StoreFlo(Flo), // Request a Flo. The FloID is in the DHTRouting::Request. - ReadFlo(RealmID), + // The U256 is a random id identifying this specific request + ReadFlo(RealmID, U256), // Request Cuckoos for the given ID. The FloID is in the DHTRouting::Request. GetCuckooIDs(RealmID), // Store the Cuckoo-ID in the relevant Flo. The DHTRouting::Request(key) is the @@ -61,7 +62,8 @@ pub enum MessageClosest { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum MessageDest { // Returns the result of the requested Flo, including any available Cuckoo-IDs. - FloValue(FloCuckoo), + // The optional u256 is an optional identifier of the readflo request id + FloValue(FloCuckoo, Option), // Indicates this Flo is not in the closest node. UnknownFlo(GlobalID), // The Cuckoo-IDs stored next to the Flo composed of the GlobalID @@ -154,6 +156,7 @@ pub struct Messages { } pub static mut EVIL_NO_FORWARD: bool = false; +pub static mut LOCAL_BLACKLISTS: bool = false; impl Messages { /// Returns a new simulation_chat module. @@ -213,7 +216,7 @@ impl Messages { DHTStorageIn::StoreFlo(flo) => self.store_flo(flo), DHTStorageIn::ReadFlo(id) => vec![match self.read_flo(&id) { Some(df) => DHTStorageOut::FloValue(df.clone()).into(), - None => MessageClosest::ReadFlo(id.realm_id().clone()) + None => MessageClosest::ReadFlo(id.realm_id().clone(), U256::rnd()) .to_intern_out(id.flo_id().clone().into()) // .inspect(|msg| log::info!("{} sends {msg:?}", self.our_id)) .expect("Creating ReadFlo message"), @@ -262,7 +265,7 @@ impl Messages { MessageClosest::StoreFlo(flo) => { return self.store_flo(flo); } - MessageClosest::ReadFlo(rid) => { + MessageClosest::ReadFlo(rid, request_id) => { // log::info!( // "{} got request for {}/{} from {}", // self.our_id, @@ -279,7 +282,7 @@ impl Messages { // log::info!("sends flo {:?}", fc.0); if unsafe { !EVIL_NO_FORWARD } { increment_stat!(self, self.experiment_stats.flo_value_sent_total); - return MessageDest::FloValue(fc) + return MessageDest::FloValue(fc, Some(request_id)) .to_intern_out(origin) // .inspect(|msg| log::info!("{} sends {msg:?}", self.our_id)) .map_or(vec![], |msg| vec![msg]); @@ -312,7 +315,7 @@ impl Messages { fn msg_dest(&mut self, msg: MessageDest) -> Vec { match msg { - MessageDest::FloValue(fc) => { + MessageDest::FloValue(fc, _) => { // log::info!("{} stores {:?}", self.our_id, flo.0); self.store_flo(fc.0.clone()); self.realms @@ -723,11 +726,11 @@ mod tests { let out = NetworkWrapper::wrap_yaml( MODULE_NAME, - &MessageDest::FloValue((fr.flo().clone(), vec![])), + &MessageDest::FloValue((fr.flo().clone(), vec![]), None), ) .unwrap(); - if let MessageDest::FloValue(flo) = out.unwrap_yaml(MODULE_NAME).unwrap() { + if let MessageDest::FloValue(flo, _) = out.unwrap_yaml(MODULE_NAME).unwrap() { let fr2 = TryInto::::try_into(flo.0)?; assert_eq!(fr, fr2); } else { From 105a1c372cba79545f254db65e23981ef3cb741b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Fri, 4 Jul 2025 01:40:06 +0200 Subject: [PATCH 42/43] feat: light and fast metrics during propagation --- cli/fledger/src/hermes/api.rs | 29 +++++++++++++++--- cli/fledger/src/hermes/update_response.rs | 2 +- cli/fledger/src/simulation_dht/simulation.rs | 25 ++++++++++++--- cli/fledger/src/state.rs | 32 ++++++++++++++++++++ 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/cli/fledger/src/hermes/api.rs b/cli/fledger/src/hermes/api.rs index 82017294..6e463789 100644 --- a/cli/fledger/src/hermes/api.rs +++ b/cli/fledger/src/hermes/api.rs @@ -16,6 +16,7 @@ use tokio::time::Instant; #[derive(Default, Clone, Debug)] pub struct HermesApi { client: reqwest::blocking::Client, + node_id: u32, } #[derive(Deserialize, Debug, Clone, Default)] @@ -32,7 +33,7 @@ impl HermesApi { Ok(bot_state) } - pub fn create_node(&self, experiment_id: u32, node_name: String) -> u32 { + pub fn create_node(&mut self, experiment_id: u32, node_name: String) -> u32 { let mut data = HashMap::new(); data.insert("name", node_name.clone()); let url = format!( @@ -42,12 +43,14 @@ impl HermesApi { let text = self.api_request(Method::POST, url, &data).unwrap(); let json: Value = serde_json::from_str(&text).unwrap(); - let id = json["id"].as_u64(); - if id.is_none() { + let id_opt = json["id"].as_u64(); + let id = if id_opt.is_none() { panic!("Failed to create node: no ID returned"); } else { - id.unwrap() as u32 - } + id_opt.unwrap() as u32 + }; + self.node_id = id; + id } pub fn get_lost_target_pages( @@ -88,6 +91,22 @@ impl HermesApi { Ok(()) } + pub fn set_node_target_pages(&self, target_pages: Vec) -> Result { + let mut data = HashMap::new(); + let ids = target_pages + .iter() + .map(|page| page.id.clone()) + .collect::>(); + data.insert("stored_targets", ids); + let url = format!( + "https://fledger.yohan.ch/api/nodes/{}/set-target-pages", + self.node_id.clone(), + ); + let response = self.api_request(Method::POST, url, &data)?; + let target_to_fetch: UpdateResponse = serde_json::from_str(&response)?; + Ok(target_to_fetch) + } + fn api_request( &self, method: Method, diff --git a/cli/fledger/src/hermes/update_response.rs b/cli/fledger/src/hermes/update_response.rs index a78d017c..e22f95cb 100644 --- a/cli/fledger/src/hermes/update_response.rs +++ b/cli/fledger/src/hermes/update_response.rs @@ -2,7 +2,7 @@ use serde::Deserialize; #[derive(Deserialize, Debug, Clone, Default)] pub struct UpdateResponse { - pub target_page_ids: Vec, + pub target_page_ids: Option>, } impl UpdateResponse {} diff --git a/cli/fledger/src/simulation_dht/simulation.rs b/cli/fledger/src/simulation_dht/simulation.rs index fdb721b3..366516f2 100644 --- a/cli/fledger/src/simulation_dht/simulation.rs +++ b/cli/fledger/src/simulation_dht/simulation.rs @@ -167,7 +167,7 @@ impl SimulationDht { f.ds.store_flo(page.flo().clone())?; wait_ms(500).await } - wait_ms(30000).await; + wait_ms(15000).await; } } @@ -258,9 +258,19 @@ impl SimulationDht { f.node.dht_storage.as_mut().unwrap().sync()?; } - if iteration % 30 == 0 { - let response = state.update_and_upload(&mut f).await; - if target_page_ids.is_empty() && !response.target_page_ids.is_empty() { + if iteration == 1 { + // initial full metrics upload, just to get some info on hermes + let _ = state.update_and_upload(&mut f).await; + } + + if iteration % 10 == 0 && target_page_ids.is_empty() { + // target propagation not over + // sending light metrics (only stored target pages) + // and checking if propagation ended + let response = state.send_target_pages(&mut f).await; + if response.target_page_ids.is_some() + && !response.target_page_ids.as_ref().unwrap().is_empty() + { // target propagation ended, it's now time to fetch. // resetting the timeout log::info!( @@ -268,7 +278,7 @@ impl SimulationDht { start_instant.elapsed().as_secs() ); start_instant = Instant::now(); - for target_page_id in response.target_page_ids { + for target_page_id in response.target_page_ids.unwrap() { target_page_ids.insert(target_page_id.clone()); } @@ -283,6 +293,11 @@ impl SimulationDht { } } + if iteration % 30 == 0 && !target_page_ids.is_empty() { + // fetching phase - full metrics upload + let _ = state.update_and_upload(&mut f).await; + } + for page_id in target_page_ids.clone() { if fetched_page_ids.contains(&page_id.clone()) { continue; diff --git a/cli/fledger/src/state.rs b/cli/fledger/src/state.rs index 78d51d02..e8445bb5 100644 --- a/cli/fledger/src/state.rs +++ b/cli/fledger/src/state.rs @@ -78,6 +78,26 @@ impl SimulationState { ); } + pub async fn get_stored_targets(&mut self, ds: &mut DHTStorage) -> Vec { + ds.get_flos() + .await + .unwrap_or_else(|e| { + log::error!("failed to get flos {e}"); + vec![] + }) + .iter() + .filter(|flo| flo.flo_type() == type_name::()) + .map(|flo| { + let page = BlobPage::from_rmp_bytes(&flo.flo_type(), &flo.data()).unwrap(); + Page { + id: flo.flo_id().to_string(), + name: page.0.values().iter().next().unwrap().1.clone(), + } + }) + .filter(|page| page.name.starts_with("target")) + .collect::>() + } + pub fn success(&mut self) { self.node_status = "success".to_string(); } @@ -107,6 +127,18 @@ impl SimulationState { self.upload() } + pub async fn send_target_pages(&mut self, f: &mut Fledger) -> UpdateResponse { + let target_pages = self + .get_stored_targets(f.node.dht_storage.as_mut().unwrap()) + .await; + self.api + .set_node_target_pages(target_pages) + .unwrap_or_else(|e| { + log::error!("failed to set target pages: {e}"); + UpdateResponse::default() + }) + } + pub fn upload(&mut self) -> UpdateResponse { let node_name = self.node_name.clone(); let _ = self From 119717a757bc65835193141a53a251bacf713746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=8Dsuke=20Aizen?= Date: Fri, 4 Jul 2025 18:34:59 +0200 Subject: [PATCH 43/43] feat: add fetch requests total metric --- cli/fledger/src/hermes/snapshot.rs | 5 +++++ cli/fledger/src/simulation_dht/simulation.rs | 1 + cli/fledger/src/state.rs | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/cli/fledger/src/hermes/snapshot.rs b/cli/fledger/src/hermes/snapshot.rs index c5037168..7980f4a3 100644 --- a/cli/fledger/src/hermes/snapshot.rs +++ b/cli/fledger/src/hermes/snapshot.rs @@ -122,6 +122,11 @@ impl Snapshot { simulation_state.target_successfully_fetched_total, ); + timeless_metrics.add_metric( + "fetch_requests_total".to_string(), + simulation_state.fetch_requests_total, + ); + Snapshot { node_status: simulation_state.node_status, pages_stored: simulation_state.pages_stored, diff --git a/cli/fledger/src/simulation_dht/simulation.rs b/cli/fledger/src/simulation_dht/simulation.rs index 366516f2..c99cb726 100644 --- a/cli/fledger/src/simulation_dht/simulation.rs +++ b/cli/fledger/src/simulation_dht/simulation.rs @@ -305,6 +305,7 @@ impl SimulationDht { let flo_id = FloID::from_str(&page_id.clone())?; let global_page_id = Self::make_page_id(realm_id.clone(), flo_id); let page = Self::fetch_page(&mut f, global_page_id).await; + state.increment_fetch_requests_total(); if page.is_ok() { fetched_page_ids.insert(page_id.clone()); } diff --git a/cli/fledger/src/state.rs b/cli/fledger/src/state.rs index e8445bb5..f58e5319 100644 --- a/cli/fledger/src/state.rs +++ b/cli/fledger/src/state.rs @@ -28,6 +28,7 @@ pub struct SimulationState { pub ds_size_bytes: u64, pub evil_no_forward: bool, pub target_successfully_fetched_total: u32, + pub fetch_requests_total: u32, pub ds_metrics: DsMetrics, @@ -48,6 +49,10 @@ impl SimulationState { state } + pub fn increment_fetch_requests_total(&mut self) { + self.fetch_requests_total += 1; + } + pub async fn refresh_pages(&mut self, ds: &mut DHTStorage) { let pages_stored = ds .get_flos()