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

Add fake mode #4

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions processor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ serde_json = "1.0.64"
valuer-client = { path = "../valuer-client" }
strum = { version = "0.20.0", features = ["derive"] }
base64 = "0.13.0"
rand = "0.8.3"
rand_chacha = "0.3.0"
105 changes: 105 additions & 0 deletions processor/src/fake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//! Pure implementation that returns valid, but fake data

use crate::{JobProgress, ProtocolSender, Request};
use judge_apis::judge_log::{JudgeLog, JudgeLogSubtaskRow, JudgeLogTestRow};
use pom::TestId;
use rand::{
distributions::{Alphanumeric, Uniform},
prelude::SliceRandom,
Rng, SeedableRng,
};
use rand_chacha::ChaChaRng;
use std::hash::{Hash, Hasher};
use tokio::sync::{mpsc, oneshot};
use valuer_api::{status_codes, JudgeLogKind, Status, StatusKind, SubtaskId};

#[derive(Clone)]
pub struct FakeSettings {}

pub fn judge(req: Request, settings: FakeSettings) -> JobProgress {
let (done_tx, done_rx) = oneshot::channel();
let (events_tx, events_rx) = mpsc::channel(1);
tokio::task::spawn(async move {
let mut protocol_sender = ProtocolSender {
sent: Vec::new(),
tx: events_tx,
debug_dump_dir: None,
};

do_judge(req, &mut protocol_sender, settings).await;

done_tx.send(Ok(())).ok();
});
JobProgress { events_rx, done_rx }
}

fn stable_hash<T: Hash + ?Sized>(val: &T) -> u64 {
let mut h = std::collections::hash_map::DefaultHasher::new();
val.hash(&mut h);
h.finish()
}

fn generate_string((len_lo, len_hi): (usize, usize), rng: &mut ChaChaRng) -> String {
let dist = Uniform::new(len_lo, len_hi);
let len = rng.sample(dist);

(0..len).map(|_| rng.sample(Alphanumeric) as char).collect()
}

fn generate_judge_log(kind: JudgeLogKind, rng: &mut ChaChaRng) -> JudgeLog {
let test_count = rng.sample(Uniform::new(3_u32, 20));
let make_status = |rng: &mut ChaChaRng| Status {
kind: [StatusKind::Accepted, StatusKind::Rejected]
.choose(&mut *rng)
.copied()
.unwrap(),
code: [
status_codes::WRONG_ANSWER,
status_codes::TEST_PASSED,
status_codes::TIME_LIMIT_EXCEEDED,
status_codes::RUNTIME_ERROR,
]
.choose(&mut *rng)
.copied()
.unwrap()
.to_owned(),
};
let tests = (0..test_count)
.map(|id| JudgeLogTestRow {
test_id: TestId::make(id + 1),
status: Some(make_status(&mut *rng)),
test_stdin: Some(generate_string((3, 100), rng)),
test_stdout: Some(generate_string((3, 100), rng)),
test_stderr: Some(generate_string((3, 100), rng)),
test_answer: Some(generate_string((3, 100), rng)),
time_usage: Some(rng.sample(Uniform::new(1_000_000, 1_000_000_000))),
memory_usage: Some(rng.sample(Uniform::new(1_000_000, 1_000_000_000))),
})
.collect();
let subtask_count = rng.sample(Uniform::new(1_u32, 10));
let subtasks = (0..subtask_count)
.map(|id| JudgeLogSubtaskRow {
subtask_id: SubtaskId::make(id + 1),
score: Some(rng.sample(Uniform::new(0, 100))),
})
.collect();
JudgeLog {
kind,
tests,
subtasks,
score: rng.sample(Uniform::new(0, 100)),
status: make_status(rng),
compile_log: (generate_string((10, 200), rng)),
is_full: false,
}
}

async fn do_judge(req: Request, protocol_sender: &mut ProtocolSender, _settings: FakeSettings) {
for kind in JudgeLogKind::list() {
let seed = stable_hash(&(&req.toolchain_name, &req.run_source, kind.as_str()));
tracing::info!(kind = kind.as_str(), seed = seed, "generating judge log");
let mut rng = ChaChaRng::seed_from_u64(seed);
let log = generate_judge_log(kind, &mut rng);
protocol_sender.send_log(log).await;
}
}
1 change: 1 addition & 0 deletions processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

mod compile;
mod exec_test;
pub mod fake;
mod request_builder;
mod transform_judge_log;

Expand Down
41 changes: 31 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ struct Args {
/// Directory containing judging logs. Set to `/dev/null` to disable logging
#[clap(long, default_value = "/var/log/judges")]
logs: PathBuf,
/// Enable fake mode.
/// In this mode judge never loads problems or toolchains and just
/// generates random data for requests
#[clap(long)]
fake: bool,
}

async fn create_clients(args: &Args) -> anyhow::Result<processor::Clients> {
Expand All @@ -54,18 +59,10 @@ async fn create_clients(args: &Args) -> anyhow::Result<processor::Clients> {
})
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let args: Args = Clap::parse();
async fn initialize_normal(args: &Args) -> anyhow::Result<rest::ServeKind> {
let clients = create_clients(&args)
.await
.context("failed to initialize dependency clients")?;
tracing::info!("Running REST API");
let cfg = rest::RestConfig { port: args.port };

let settings = {
let checker_logs = match &args.logs {
p if p == Path::new("/dev/null") => (None),
Expand All @@ -81,6 +78,30 @@ async fn main() -> anyhow::Result<()> {
}
processor::Settings { checker_logs }
};
rest::serve(cfg, clients, settings).await?;
Ok(rest::ServeKind::Normal { settings, clients })
}

fn initialize_fake() -> rest::ServeKind {
rest::ServeKind::Fake {
settings: processor::fake::FakeSettings {},
}
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let args: Args = Clap::parse();
tracing::info!("Running REST API");
let cfg = rest::RestConfig { port: args.port };

let serve_config = if args.fake {
initialize_fake()
} else {
initialize_normal(&args).await?
};

rest::serve(cfg, serve_config).await?;
Ok(())
}
46 changes: 28 additions & 18 deletions src/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,19 @@ impl JudgeJob {
}
}

pub enum ServeKind {
Normal {
clients: processor::Clients,
settings: processor::Settings,
},
Fake {
settings: processor::fake::FakeSettings,
},
}

struct State {
judge: RwLock<HashMap<Uuid, Arc<Mutex<JudgeJob>>>>,
clients: processor::Clients,
settings: processor::Settings,
kind: ServeKind,
}

async fn start_job(
Expand All @@ -58,15 +67,21 @@ async fn start_job(
run_source: req.run_source.0,
};
let job_id = Uuid::new_v4();
let mut settings = state.settings.clone();
{
let mut job_id_s = Uuid::encode_buffer();
let job_id_s = job_id.to_hyphenated().encode_lower(&mut job_id_s);
if let Some(p) = &mut settings.checker_logs {
p.push(&*job_id_s);

let mut progress = match &state.kind {
ServeKind::Normal { settings, clients } => {
let mut settings = settings.clone();
{
let mut job_id_s = Uuid::encode_buffer();
let job_id_s = job_id.to_hyphenated().encode_lower(&mut job_id_s);
if let Some(p) = &mut settings.checker_logs {
p.push(&*job_id_s);
}
}
processor::judge(proc_request, clients.clone(), settings)
}
}
let mut progress = processor::judge(proc_request, state.clients.clone(), settings);
ServeKind::Fake { settings } => processor::fake::judge(proc_request, settings.clone()),
};
let job = JudgeJob {
id: job_id,
live_test: None,
Expand Down Expand Up @@ -154,16 +169,11 @@ async fn get_job_judge_log(
}

/// Serves api
#[tracing::instrument(skip(cfg, clients, settings))]
pub async fn serve(
cfg: RestConfig,
clients: processor::Clients,
settings: processor::Settings,
) -> anyhow::Result<()> {
#[tracing::instrument(skip(cfg, kind))]
pub async fn serve(cfg: RestConfig, kind: ServeKind) -> anyhow::Result<()> {
let state = Arc::new(State {
judge: RwLock::new(HashMap::new()),
clients,
settings,
kind,
});
let state2 = state.clone();
let route_create_job = warp::post()
Expand Down