Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Storage Benchmarking #38

Draft
wants to merge 26 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e32d450
(feat): trace-based workloads
fzovpec Nov 20, 2024
bc1c3f1
(feat): updated monitoring: disk statitic collection script
fzovpec Nov 25, 2024
f0c9358
(refactor): trace based emulation error handling
fzovpec Nov 25, 2024
75125a3
Recording response time at another player
fzovpec Nov 25, 2024
9c7fc45
Provisioning virtual storage
fzovpec Nov 26, 2024
7909aa6
Added an ssh option
fzovpec Nov 26, 2024
61a3b63
Added ansible ssh password and port
fzovpec Nov 26, 2024
f8861df
Added support for virtualized storage
fzovpec Nov 26, 2024
1b09b92
Added switching between sudo and non-sudo modes to all playbooks and …
fzovpec Nov 26, 2024
53aba9f
Fix for telegraf to run under sudo as well
fzovpec Nov 27, 2024
2a876fc
Player simulation
fzovpec Nov 28, 2024
f0c26c6
Remove .DS_Store files
fzovpec Nov 28, 2024
5267d68
Deleted DS_Store files
fzovpec Nov 28, 2024
8c95346
Added support fetching from remote nodes
fzovpec Nov 29, 2024
e1f0109
Use optionally strace
fzovpec Dec 2, 2024
00bca6e
added an option for sudo through extravars
fzovpec Dec 2, 2024
d77de4b
Logging action time
fzovpec Dec 2, 2024
cd34d5d
Trace bot debug on for attack player action
fzovpec Dec 4, 2024
1952eff
Adding copying world data
fzovpec Dec 6, 2024
58883b7
Added world
fzovpec Dec 6, 2024
f2fc1c4
added storage debugging
fzovpec Dec 11, 2024
27e1a62
Different cleanup + delete diskstat from monitoring
fzovpec Dec 11, 2024
b96a77c
Remove all .DS_Store files
fzovpec Dec 11, 2024
274ad50
World setup + debugging placing blocks
fzovpec Jan 8, 2025
2d937f4
Remove .DS_Store files
fzovpec Jan 8, 2025
b382406
Added teleport action
fzovpec Jan 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion yardstick_benchmark/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,40 @@
from yardstick_benchmark.model import Node, RemoteAction
from pathlib import Path
import os
import tarfile
from pathlib import Path

def unarchive(src: Path, dest: Path = None):
"""
Extract all .tar.gz archives in the specified source directory.

Parameters:
- src (Path): The path to the directory containing .tar.gz files.
- dest (Path, optional): The destination directory for extraction.
Defaults to the current working directory.
"""
src = Path(src)
dest = Path(dest) if dest else Path.cwd()

if not src.is_dir():
raise FileNotFoundError(f"Source directory {src} does not exist.")

dest.mkdir(parents=True, exist_ok=True)
tar_files = src.glob("*.tar.gz")

for tar_file in tar_files:
try:
with tarfile.open(tar_file, "r:gz") as tar:
if tar_file.suffixes[-2:] == [".tar", ".gz"]:
extract_name = tar_file.name.removesuffix(".tar.gz")
else:
extract_name = tar_file.stem
extract_path = dest / extract_name
extract_path.mkdir(parents=True, exist_ok=True)
tar.extractall(path=extract_path)
print(f"Extracted {tar_file} to {extract_path}")
except tarfile.TarError as e:
print(f"Failed to extract {tar_file}: {e}")

def fetch(dest: Path, nodes: list[Node]):
dest.mkdir(parents=True, exist_ok=True)
Expand All @@ -11,7 +45,6 @@ def fetch(dest: Path, nodes: list[Node]):
extravars={"dest": str(dest)},
).run()


def clean(nodes: list[Node]):
return RemoteAction(
"clean",
Expand Down
3 changes: 3 additions & 0 deletions yardstick_benchmark/clean.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
- name: Clean data from nodes
gather_facts: true
hosts: all
become: "{{ lookup('env', 'ANSIBLE_USE_SUDO') | default('false') == 'true' }}"
become_method: sudo
become_user: root
tasks:
- name: Remove data from nodes
file:
Expand Down
24 changes: 17 additions & 7 deletions yardstick_benchmark/fetch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
gather_facts: true
hosts: all
tasks:
- name: Get data from nodes
ansible.posix.synchronize:
src: "{{node_wd}}"
dest: "{{dest}}"
mode: pull
- debug:
msg: "Output files have been written to {{dest}}"
- name: Archive the directory
ansible.builtin.archive:
path: "{{node_wd}}"
dest: "{{node_wd}}.tar.gz"
format: gz

- name: Fetch the archive
ansible.builtin.fetch:
src: "{{node_wd}}.tar.gz"
dest: "{{dest}}/"
flat: yes

- name: Remove the archive file
ansible.builtin.file:
path: "{{node_wd}}.tar.gz"
state: absent

5 changes: 4 additions & 1 deletion yardstick_benchmark/games/minecraft/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class PaperMC(RemoteApplication):
def __init__(self, nodes: list[Node]):
def __init__(self, nodes: list[Node], world_dir_path='', copy_player_data=False, use_strace=False):
super().__init__(
"papermc",
nodes,
Expand All @@ -15,5 +15,8 @@ def __init__(self, nodes: list[Node]):
extravars={
"hostnames": [n.host for n in nodes],
"papermc_template": str(Path(__file__).parent / "server.properties.j2"),
"use_strace": use_strace,
"world_dir_path": world_dir_path,
"copy_player_data": copy_player_data
},
)
13 changes: 13 additions & 0 deletions yardstick_benchmark/games/minecraft/server/papermc_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
- name: Deploy PaperMC
gather_facts: true
hosts: all
become: "{{ use_sudo }}"
tasks:
- file:
path: "{{ wd }}"
Expand All @@ -22,3 +23,15 @@
get_url:
url: https://search.maven.org/remotecontent?filepath=org/jolokia/jolokia-agent-jvm/2.0.3/jolokia-agent-jvm-2.0.3-javaagent.jar
dest: "{{wd}}/jolokia-agent-jvm-2.0.3-javaagent.jar"

- name: Copy world dir to destination
copy:
src: "{{ world_dir_path }}"
dest: "{{ wd }}"
when: world_dir_path is defined and world_dir_path | length > 0

- name: Delete playerdata subdirectory if necessary
file:
path: "{{ wd }}/world/playerdata"
state: absent
when: copy_player_data != true
29 changes: 27 additions & 2 deletions yardstick_benchmark/games/minecraft/server/papermc_start.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,40 @@
- name: Deploy PaperMC
gather_facts: true
hosts: all
become: "{{ use_sudo }}"
tasks:
- name: Run PaperMC
shell:
cmd: |
module load java/jdk-17 || true # just in case we are on DAS
nohup java -javaagent:jolokia-agent-jvm-2.0.3-javaagent.jar -jar paper-1.20.1-58.jar &> /dev/null &
echo $! > papermc.pid
nohup sh -c "{{ 'strace -f -ttt -T -e trace=pread64,pwrite64,read,write,openat,close -o fsysstrace.log' if use_strace else '' }} java -Xms16G -Xmx16G -javaagent:jolokia-agent-jvm-2.0.3-javaagent.jar -jar paper-1.20.1-58.jar" {{ '' if use_strace else '> /dev/null' }} &
NOHUP_PID=$!
echo $NOHUP_PID > nohup.pid
sleep 2
{% if use_strace %}
STRACE_PID=$(pgrep -P $NOHUP_PID -x 'strace.*')
JAVA_PID=$(pgrep -P $STRACE_PID -x 'java.*')
echo $JAVA_PID > papermc.pid
{% else %}
JAVA_PID=$(pgrep -P $NOHUP_PID -x 'java.*')
echo $JAVA_PID > papermc.pid
{% endif %}
chdir: "{{ wd }}"
- name: Wait for the latest.log file to appear
wait_for:
path: "{{ wd }}/logs/latest.log"
state: present
timeout: 60
- name: Waiting for server to become ready
wait_for:
timeout: 60
path: "{{wd}}/logs/latest.log"
search_regex: 'For help, type "help"'
when: use_sudo != true
- name: Waiting for server to become ready using cat (used due to compatibility issue with nullblk)
shell: |
while ! cat "{{ wd }}/logs/latest.log" | grep -q 'For help, type "help"'; do
sleep 0.05
done
timeout: 60
when: use_sudo == true
6 changes: 4 additions & 2 deletions yardstick_benchmark/games/minecraft/server/papermc_stop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
- name: Stop PaperMC
gather_facts: true
hosts: all
become: "{{ use_sudo }}"
tasks:
- name: Find PaperMC PID
shell: |
cat {{wd}}/papermc.pid
shell:
cmd: cat papermc.pid
chdir: "{{ wd }}"
register: shell_papermc_pid
- name: Obtain PaperMC pid
set_fact:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#Mon Apr 29 15:15:15 CEST 2024
enable-jmx-monitoring=true
rcon.port=25575
level-seed=
gamemode=survival
level-seed=-3691007458655063350
gamemode=creative
enable-command-block=false
enable-query=false
generator-settings={}
Expand All @@ -25,7 +25,7 @@ enable-status=true
allow-flight=true
initial-disabled-packs=
broadcast-rcon-to-ops=true
view-distance=10
view-distance=32
server-ip=
resource-pack-prompt=
allow-nether=true
Expand Down Expand Up @@ -56,4 +56,6 @@ spawn-monsters=false
enforce-whitelist=false
spawn-protection=16
resource-pack-sha1=
sync-chunk-writes=true
region-file-compression=none
max-world-size=29999984
30 changes: 30 additions & 0 deletions yardstick_benchmark/games/minecraft/workload/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,33 @@ def __init__(
"bots_per_node": bots_per_node,
},
)

class TraceBased(RemoteApplication):
def __init__(
self,
nodes: list[Node],
server_host: str,
trace_file: str,
duration: timedelta = timedelta(seconds=60)
):
super().__init__(
"trace_based",
nodes,
Path(__file__).parent / "trace_deploy.yml",
Path(__file__).parent / "trace_start.yml",
Path(__file__).parent / "trace_stop.yml",
Path(__file__).parent / "trace_cleanup.yml",
extravars={
"hostnames": [n.host for n in nodes],
"mc_host": server_host,
"scripts": [
str(Path(__file__).parent / "trace.js"),
str(Path(__file__).parent / "trace_bot.js"),
],
"trace": [
trace_file,
],
"trace_file": Path(trace_file).name,
"duration": duration.total_seconds()
},
)
103 changes: 103 additions & 0 deletions yardstick_benchmark/games/minecraft/workload/trace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const fs = require('node:fs');
const readline = require('readline');
const { Worker, isMainThread } = require("worker_threads");
const { writeFile } = require('node:fs/promises');
const Rcon = require('rcon');

const host = process.env.MC_HOST;
var rcon = new Rcon(host, 25575, 'password');
const trace = process.env.TRACE;

const trace_start_timestamp = parseInt(process.env.START_TIMESTAMP);
const timeout_s = parseInt(process.env.DURATION);

const players = {};

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function tp(player, arguments){
const [x, z] = arguments;
console.log('hello');
rcon.send(`tp ${player} ${x} -60 ${z}`);
}

async function run(){
await writeFile('issued.csv', 'timestamp, player_name, action \n');
await writeFile('received.csv', 'timestamp, player_name, issuer_name, action \n');

console.log('script starts');

rcon.on('auth', async () => {
const fileStream = fs.createReadStream(trace);
const rl = readline.createInterface({
input: fileStream,
output: process.stdout
});

const benchmark_starting_time = new Date();
console.log('authenticated');
let benchmark_starting_timestamp = benchmark_starting_time.valueOf();

rl.on("line", async (line) => {
let [timestamp, player, action, ...arguments] = line.split(' ');

timestamp = Number(timestamp);

while(true) {
const current_time = new Date();
let current_timestamp = current_time.valueOf();

if((current_timestamp - benchmark_starting_timestamp) > (timestamp - trace_start_timestamp)){
break;
}
await sleep(1000);
console.log('hello???');
}

if(action == 'tp'){
tp(player, arguments);
} else if(!players[player]){
console.log('initalizing a new one!');
const worker = new Worker("./trace_bot.js");
players[player] = worker;

worker.on('message', (result) => {
console.log(`Received from worker (player: ${player}): ${result}`);
});

worker.on('error', (error) => {
console.error(`Worker error for player ${player}:`, error);
});

worker.on('exit', (code) => {
if (code !== 0) {
console.error(`Worker stopped with exit code ${code} for player ${player}`);
}
});

players[player].postMessage({ action, arguments: [player] });
} else{
players[player].postMessage({ action, arguments });
}
});
});

rcon.on('end', () => {
console.log("RCON connection closed.");
});

rcon.on('error', (err) => {
console.error("RCON error:", err);
});

rcon.connect();

await sleep(timeout_s * 1000)
process.exit(0);
}

if (isMainThread) {
run();
}
Loading