Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
varqox committed Jun 6, 2024
2 parents 86b80c2 + 7320e20 commit ce5187d
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 209 deletions.
50 changes: 0 additions & 50 deletions subprojects/sim/include/sim/jobs/utils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -78,36 +78,6 @@ inline void extract_dumped(std::optional<T>& opt, StringView& dumped_str) {
}
}

struct MergeProblemsInfo {
decltype(sim::problems::OldProblem::id) target_problem_id{};
static_assert(
sizeof(target_problem_id) == 8, "Changing size needs updating column info in jobs table"
);
bool rejudge_transferred_submissions{};

MergeProblemsInfo() = default;

MergeProblemsInfo(decltype(sim::problems::OldProblem::id) tpid, bool rts) noexcept
: target_problem_id(tpid)
, rejudge_transferred_submissions(rts) {}

explicit MergeProblemsInfo(StringView str) {
extract_dumped(target_problem_id, str);

auto mask = extract_dumped_int<uint8_t>(str);
rejudge_transferred_submissions = (mask & 1);
}

[[nodiscard]] std::string dump() const {
std::string res;
append_dumped(res, target_problem_id);

uint8_t mask = rejudge_transferred_submissions;
append_dumped(res, mask);
return res;
}
};

struct ChangeProblemStatementInfo {
std::string new_statement_path;

Expand All @@ -118,26 +88,6 @@ struct ChangeProblemStatementInfo {
[[nodiscard]] std::string dump() const { return new_statement_path; }
};

struct MergeUsersInfo {
decltype(sim::users::User::id) target_user_id{};
static_assert(
sizeof(target_user_id) == 8, "Changing size needs updating column info in jobs table"
);

MergeUsersInfo() = default;

explicit MergeUsersInfo(decltype(sim::users::User::id) target_uid) noexcept
: target_user_id(target_uid) {}

explicit MergeUsersInfo(StringView str) { extract_dumped(target_user_id, str); }

[[nodiscard]] std::string dump() const {
std::string res;
append_dumped(res, target_user_id);
return res;
}
};

void restart_job(mysql::Connection& mysql, StringView job_id, bool notify_job_server);

// Notifies the Job server that there are jobs to do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <sim/jobs/job.hh>

namespace sim::merge_problems_jobs {

struct MergeProblemsJob {
decltype(jobs::Job::id) id;
bool rejudge_transferred_submissions;
};

} // namespace sim::merge_problems_jobs
6 changes: 1 addition & 5 deletions subprojects/sim/src/job_server/dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,7 @@ void job_dispatcher(
job_handler = make_unique<DeleteProblem>(job_id, aux_id.value());
break;

case JT::MERGE_PROBLEMS:
job_handler = make_unique<MergeProblems>(
job_id, aux_id.value(), sim::jobs::MergeProblemsInfo(info)
);
break;
case JT::MERGE_PROBLEMS: job_handler = make_unique<MergeProblems>(job_id); break;

case JT::DELETE_USER: job_handler = make_unique<DeleteUser>(job_id, aux_id.value()); break;

Expand Down
210 changes: 108 additions & 102 deletions subprojects/sim/src/job_server/job_handlers/merge_problems.cc
Original file line number Diff line number Diff line change
@@ -1,157 +1,163 @@
#include "merge_problems.hh"

#include <deque>
#include <sim/submissions/old_submission.hh>
#include <sim/jobs/job.hh>
#include <sim/merge_problems_jobs/merge_problems_job.hh>
#include <sim/problems/problem.hh>
#include <sim/sql/sql.hh>
#include <sim/submissions/submission.hh>
#include <sim/submissions/update_final.hh>

using sim::jobs::OldJob;
using sim::submissions::OldSubmission;
#include <simlib/throw_assert.hh>
#include <simlib/time.hh>

using sim::jobs::Job;
using sim::merge_problems_jobs::MergeProblemsJob;
using sim::problems::Problem;
using sim::sql::DeleteFrom;
using sim::sql::InsertInto;
using sim::sql::Select;
using sim::sql::Update;
using sim::submissions::Submission;

namespace job_server::job_handlers {

void MergeProblems::run(sim::mysql::Connection& mysql) {
STACK_UNWINDING_MARK;

for (;;) {
try {
run_impl(mysql);
break;
} catch (const std::exception& e) {
if (has_prefix(
e.what(),
"Deadlock found when trying to get lock; "
"try restarting transaction"
))
{
continue;
}

throw;
}
}
}

void MergeProblems::run_impl(sim::mysql::Connection& mysql) {
STACK_UNWINDING_MARK;

auto transaction = mysql.start_repeatable_read_transaction();
auto old_mysql = old_mysql::ConnectionView{mysql};

decltype(Job::aux_id)::value_type donor_problem_id;
decltype(Job::aux_id_2)::value_type target_problem_id;
decltype(MergeProblemsJob::rejudge_transferred_submissions) rejudge_transferred_submissions;
{
auto stmt =
mysql.execute(Select("j.aux_id, j.aux_id_2, mpj.rejudge_transferred_submissions")
.from("jobs j")
.inner_join("merge_problems_jobs mpj")
.on("mpj.id=j.id")
.where("j.id=?", job_id_));
stmt.res_bind(donor_problem_id, target_problem_id, rejudge_transferred_submissions);
throw_assert(stmt.next());
}

// Assure that both problems exist
{
auto stmt = old_mysql.prepare("SELECT simfile FROM problems WHERE id=?");
stmt.bind_and_execute(donor_problem_id_);
InplaceBuff<0> simfile;
stmt.res_bind_all(simfile);
auto stmt =
mysql.execute(Select("simfile").from("problems").where("id=?", donor_problem_id));
decltype(Problem::simfile) donor_problem_simfile;
stmt.res_bind(donor_problem_simfile);
if (not stmt.next()) {
return set_failure("Problem does not exist");
return set_failure("Problem to delete does not exist");
}

job_log("Merged problem Simfile:\n", simfile);

stmt = old_mysql.prepare("SELECT 1 FROM problems WHERE id=?");
stmt.bind_and_execute(info_.target_problem_id);
job_log("Merged problem (donor) Simfile:\n", donor_problem_simfile);
}
{
auto stmt = mysql.execute(Select("1").from("problems").where("id=?", target_problem_id));
int x;
stmt.res_bind_all(x);
stmt.res_bind(x);
if (not stmt.next()) {
return set_failure("Target problem does not exist");
}
}

// Transfer contest problems
old_mysql.prepare("UPDATE contest_problems SET problem_id=? WHERE problem_id=?")
.bind_and_execute(info_.target_problem_id, donor_problem_id_);
mysql.execute(Update("contest_problems")
.set("problem_id=?", target_problem_id)
.where("problem_id=?", donor_problem_id));

auto current_utc_datetime = utc_mysql_datetime();

// Add job to delete problem file
old_mysql
.prepare("INSERT INTO jobs(file_id, creator, type, priority, status,"
" created_at, aux_id, info, data) "
"SELECT file_id, NULL, ?, ?, ?, ?, NULL, '', '' "
"FROM problems WHERE id=?")
.bind_and_execute(
EnumVal(OldJob::Type::DELETE_FILE),
default_priority(OldJob::Type::DELETE_FILE),
EnumVal(OldJob::Status::PENDING),
utc_mysql_datetime(),
donor_problem_id_
);
mysql.execute(
InsertInto("jobs(file_id, creator, type, priority, status, created_at, aux_id, info, data)")
.select(
"file_id, NULL, ?, ?, ?, ?, NULL, '', ''",
Job::Type::DELETE_FILE,
default_priority(Job::Type::DELETE_FILE),
Job::Status::PENDING,
current_utc_datetime
)
.from("problems")
.where("id=?", donor_problem_id)
);

// Add jobs to delete problem solutions' files
old_mysql
.prepare("INSERT INTO jobs(file_id, creator, type, priority, status,"
" created_at, aux_id, info, data) "
"SELECT file_id, NULL, ?, ?, ?, ?, NULL, '', '' "
"FROM submissions WHERE problem_id=? AND "
"type=?")
.bind_and_execute(
EnumVal(OldJob::Type::DELETE_FILE),
default_priority(OldJob::Type::DELETE_FILE),
EnumVal(OldJob::Status::PENDING),
utc_mysql_datetime(),
donor_problem_id_,
EnumVal(OldSubmission::Type::PROBLEM_SOLUTION)
);
mysql.execute(
InsertInto("jobs(file_id, creator, type, priority, status, created_at, aux_id, info, data)")
.select(
"file_id, NULL, ?, ?, ?, ?, NULL, '', ''",
Job::Type::DELETE_FILE,
default_priority(Job::Type::DELETE_FILE),
Job::Status::PENDING,
current_utc_datetime
)
.from("submissions")
.where("problem_id=? AND type=?", donor_problem_id, Submission::Type::PROBLEM_SOLUTION)
);

// Delete problem solutions
old_mysql
.prepare("DELETE FROM submissions "
"WHERE problem_id=? AND type=?")
.bind_and_execute(donor_problem_id_, EnumVal(OldSubmission::Type::PROBLEM_SOLUTION));
mysql.execute(
DeleteFrom("submissions")
.where("problem_id=? AND type=?", donor_problem_id, Submission::Type::PROBLEM_SOLUTION)
);

// Collect update finals
struct FTU {
old_mysql::Optional<uint64_t> user_id;
old_mysql::Optional<uint64_t> contest_problem_id;
struct FinalToUpdate {
decltype(Submission::user_id) user_id = 0;
decltype(Submission::contest_problem_id) contest_problem_id;
};

std::deque<FTU> finals_to_update;
std::vector<FinalToUpdate> finals_to_update;
{
auto stmt = old_mysql.prepare("SELECT DISTINCT user_id, contest_problem_id "
"FROM submissions WHERE problem_id=?");
stmt.bind_and_execute(donor_problem_id_);
FTU ftu_elem;
stmt.res_bind_all(ftu_elem.user_id, ftu_elem.contest_problem_id);
auto stmt = mysql.execute(Select("DISTINCT user_id, contest_problem_id")
.from("submissions")
.where("problem_id=?", donor_problem_id));
FinalToUpdate ftu;
stmt.res_bind(ftu.user_id, ftu.contest_problem_id);
while (stmt.next()) {
finals_to_update.emplace_back(ftu_elem);
finals_to_update.emplace_back(ftu);
}
}

// Schedule rejudge of the transferred submissions
if (info_.rejudge_transferred_submissions) {
old_mysql
.prepare("INSERT INTO jobs(creator, status, priority, type, created_at,"
" aux_id, info, data) "
"SELECT NULL, ?, ?, ?, ?, id, '', '' "
"FROM submissions WHERE problem_id=? ORDER BY id")
.bind_and_execute(
EnumVal(OldJob::Status::PENDING),
default_priority(OldJob::Type::REJUDGE_SUBMISSION),
EnumVal(OldJob::Type::REJUDGE_SUBMISSION),
utc_mysql_datetime(),
donor_problem_id_
);
if (rejudge_transferred_submissions) {
mysql.execute(
InsertInto("jobs (creator, type, priority, status, created_at, aux_id, info, data)")
.select(
"NULL, ?, ?, ?, ?, id, '', ''",
Job::Type::REJUDGE_SUBMISSION,
default_priority(Job::Type::REJUDGE_SUBMISSION),
Job::Status::PENDING,
current_utc_datetime
)
.from("submissions")
.where("problem_id=?", donor_problem_id)
.order_by("id")
);
}

// Transfer problem submissions that are not problem solutions
old_mysql.prepare("UPDATE submissions SET problem_id=? WHERE problem_id=?")
.bind_and_execute(info_.target_problem_id, donor_problem_id_);
mysql.execute(Update("submissions")
.set("problem_id=?", target_problem_id)
.where("problem_id=?", donor_problem_id));

// Update finals (both contest and problem finals are being taken care of)
for (const auto& ftu_elem : finals_to_update) {
sim::submissions::update_final_lock(mysql, ftu_elem.user_id, info_.target_problem_id);
for (const auto& ftu : finals_to_update) {
sim::submissions::update_final_lock(mysql, ftu.user_id, target_problem_id);
sim::submissions::update_final(
mysql, ftu_elem.user_id, info_.target_problem_id, ftu_elem.contest_problem_id, false
mysql, ftu.user_id, target_problem_id, ftu.contest_problem_id, false
);
}

// Transfer problem tags (duplicates will not be transferred - they will be
// deleted on problem deletion)
old_mysql.prepare("UPDATE IGNORE problem_tags SET problem_id=? WHERE problem_id=?")
.bind_and_execute(info_.target_problem_id, donor_problem_id_);
// deleted on problem deletion thanks to foreign key constraints)
mysql.execute(Update("IGNORE problem_tags")
.set("problem_id=?", target_problem_id)
.where("problem_id=?", donor_problem_id));

// Finally, delete the donor problem (solution and remaining tags will be
// deleted automatically thanks to foreign key constraints)
old_mysql.prepare("DELETE FROM problems WHERE id=?").bind_and_execute(donor_problem_id_);
mysql.execute(DeleteFrom("problems").where("id=?", donor_problem_id));

job_done(mysql);
transaction.commit();
Expand Down
10 changes: 1 addition & 9 deletions subprojects/sim/src/job_server/job_handlers/merge_problems.hh
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,8 @@
namespace job_server::job_handlers {

class MergeProblems final : public JobHandler {
const uint64_t donor_problem_id_;
const sim::jobs::MergeProblemsInfo info_;

public:
MergeProblems(
uint64_t job_id, uint64_t donor_problem_id, const sim::jobs::MergeProblemsInfo& info
)
: JobHandler(job_id)
, donor_problem_id_(donor_problem_id)
, info_(info) {}
explicit MergeProblems(uint64_t job_id) : JobHandler(job_id) {}

void run(sim::mysql::Connection& mysql) final;

Expand Down
Loading

0 comments on commit ce5187d

Please sign in to comment.