diff --git a/src/users/user_feedback.cpp b/src/users/user_feedback.cpp index b4fb6bc6d8bee70e59aba6257f334ca355a608fc..2d539f95665a08806139cb6286fffd06e01c728d 100644 --- a/src/users/user_feedback.cpp +++ b/src/users/user_feedback.cpp @@ -2,6 +2,7 @@ #include "../pempek_assert.hpp" #include "json_workload.hpp" #include "rapidjson/document.h" +#include <limits> #include <string> /* FeedbackUser */ @@ -42,78 +43,108 @@ FeedbackUser::~FeedbackUser() { } +void FeedbackUser::update_date_next_sub() +{ + /* Calculate the date of next submission. + * next_submit_time = DATE_UNKNOWN || DATE_NEVER || + min(free_session->next, active_session->next) */ + + if (active_sessions.empty()){ + if (!free_sessions.empty()) + date_of_next_submission = free_sessions.top()->start_time; + else if (ongoing_job_counter > 0) + date_of_next_submission = DATE_UNKNOWN; + else + date_of_next_submission = DATE_NEVER; + return; + } + + /* Case active_sessions not empty: the next submit time is known. + * Calculate the min. */ + date_of_next_submission = std::numeric_limits<double>::max(); + for (Session *active_sess : active_sessions) + { + PPK_ASSERT_ERROR(!active_sess->jobs.empty(), + "Active session %d has no job to submit", active_sess->id); + + double next_call = active_sess->start_time + + active_sess->jobs.front()->submission_time; + + date_of_next_submission = std::min(date_of_next_submission, next_call); + } + + if (!free_sessions.empty()) + date_of_next_submission = std::min(date_of_next_submission, + free_sessions.top()->start_time); + return; +} + void FeedbackUser::jobs_to_submit( double date, std::list<Job *> &jobs, std::list<Profile *> &profiles) { jobs = std::list<Job *>(); profiles = std::list<Profile *>(); - if (active_session == nullptr) + /* Add the free sessions starting now to the list of active sessions */ + while (!free_sessions.empty() && free_sessions.top()->start_time <= date) { - PPK_ASSERT_ERROR(!free_sessions.empty(), - "User %s has been called to sumbit but she has neither an active " - "session nor free sessions", - user_name.c_str()); - PPK_ASSERT_ERROR(free_sessions.top()->start_time <= date, - "Next free session of user %s has greater start time than " - "current date", - user_name.c_str()); - - active_session = free_sessions.top(); + active_sessions.push_back(free_sessions.top()); free_sessions.pop(); } - - auto job_list = &active_session->jobs; - PPK_ASSERT_ERROR(!job_list->empty(), - "User %s has been called to sumbit but the job list in her active " - "session is empty", + PPK_ASSERT_ERROR(!active_sessions.empty(), + "User %s has been called to sumbit but she has no active session", user_name.c_str()); - double offset = active_session->start_time; - PPK_ASSERT_ERROR(job_list->front()->submission_time + offset <= date, - "First job in user %s's job list has greater sumbmission time than " - "current date", - user_name.c_str()); - /* Submit all jobs that have same submit time */ - while (!job_list->empty() - && job_list->front()->submission_time + offset <= date) + /* For each active session, add the jobs to submit now to the list `jobs` */ + for (auto iter = active_sessions.begin(); iter != active_sessions.end();) { - Job *job = new Job(*job_list->front()); /* Cast const Job * -> Job * */ - Profile *job_profile = new Profile(); + Session *active_sess = *iter; + auto job_list = &active_sess->jobs; + PPK_ASSERT_ERROR(!job_list->empty(), + "Active session %d has no job to submit", active_sess->id); - /* Handle the job according to the user behavior. */ - bool execute_now = handle_job(date, job, job_profile); + double offset = active_sess->start_time; - if (execute_now) + while (!job_list->empty() + && job_list->front()->submission_time + offset <= date) { - job->submission_time = date; - /* Add it to the list of jobs (and profiles, if new) to submit */ - jobs.push_back(job); - if (sent_profiles.count(job->profile) == 0) + Job *job = new Job(*job_list->front()); // Cast const Job * -> Job * + Profile *job_profile = new Profile(); + + /* Handle the job according to the user behavior. */ + bool execute_now = handle_job(date, job, job_profile); + + if (execute_now) { - profiles.push_back(job_profile); - sent_profiles.insert(job->profile); + job->submission_time = date; + /* Add it to the list of jobs (and profiles, if new) to submit */ + jobs.push_back(job); + if (sent_profiles.count(job->profile) == 0) + { + profiles.push_back(job_profile); + sent_profiles.insert(job->profile); + } + ongoing_job_counter++; + + /* Delete job from the job list */ + job_list->pop_front(); } - ongoing_job_counter++; - - /* Delete job from the job list */ - job_list->pop_front(); } - } - /* Update next submit time */ - if (!job_list->empty()) - date_of_next_submission = job_list->front()->submission_time + offset; - else - { - active_session = nullptr; - - if (!free_sessions.empty()) - date_of_next_submission = free_sessions.top()->start_time; + if (job_list->empty()) /* active session finished: close it */ + iter = active_sessions.erase(iter); else - date_of_next_submission = DATE_UNKNOWN; + ++iter; } + + PPK_ASSERT_ERROR(!jobs.empty(), + "User %s called to submit but did not submit any job", + user_name.c_str()); + + + /* Update next submit time */ + update_date_next_sub(); } void FeedbackUser::wake_on_feedback(double date, std::list<Job *> &ended_jobs) @@ -131,16 +162,8 @@ void FeedbackUser::wake_on_feedback(double date, std::list<Job *> &ended_jobs) close_session(date, j->session); } - /* If no active session, find the next submit time */ - if (active_session == nullptr) - { - if (!free_sessions.empty()) - date_of_next_submission = free_sessions.top()->start_time; - else if (ongoing_job_counter > 0) - date_of_next_submission = DATE_UNKNOWN; - else - date_of_next_submission = DATE_NEVER; - } + /* The next submit time might have changed in reaction to feedback */ + update_date_next_sub(); } /* FBUserThinkTimeOnly */ diff --git a/src/users/user_feedback.hpp b/src/users/user_feedback.hpp index b5565d921ab57911e92dafef92d0739d52a56b2a..4a60ae0a15747daa0d6f9fcbb8e4a529d5b95336 100644 --- a/src/users/user_feedback.hpp +++ b/src/users/user_feedback.hpp @@ -28,6 +28,11 @@ protected: */ void init_FeedbackUser(std::string name, const rapidjson::Value ¶m); + /** + * Method to update the date of the next submission of the user + */ + void update_date_next_sub(); + /** * A session has finished (all its jobs finished). Update its following * sessions and the free sessions depending on the replay model. @@ -47,9 +52,8 @@ protected: std::set<std::string> sent_profiles; unsigned int ongoing_job_counter = 0; - /* A session is "active" if the user is currently submitting from it. - * There is only one active session at a time. */ - Session *active_session = nullptr; + /* A session is "active" if the user is currently submitting from it */ + std::list<Session *> active_sessions; /* A session is "free" if its dependancy list is empty and it hasn't started * yet */ diff --git a/test/expected_log/fb_user_think_time_only-simultaneous_active_sessions_jobs.csv b/test/expected_log/fb_user_think_time_only-simultaneous_active_sessions_jobs.csv new file mode 100644 index 0000000000000000000000000000000000000000..df25af899915704942784adf87ad7b437c6373d1 --- /dev/null +++ b/test/expected_log/fb_user_think_time_only-simultaneous_active_sessions_jobs.csv @@ -0,0 +1,6 @@ +job_id,workload_name,profile,submission_time,requested_number_of_resources,requested_time,success,final_state,starting_time,execution_time,finish_time,waiting_time,turnaround_time,stretch,allocated_resources,consumed_energy,metadata +1:s1,fb_user_think_time_only,2400,0.000000,8,2592000.000000,1,COMPLETED_SUCCESSFULLY,0.000000,2400.000000,2400.000000,0.000000,2400.000000,1.000000,0,380400.000000,"" +4:s3,fb_user_think_time_only,4800,4800.000000,8,2592000.000000,1,COMPLETED_SUCCESSFULLY,4800.000000,4800.000000,9600.000000,0.000000,4800.000000,1.000000,0,971400.000000,"" +5:s3,fb_user_think_time_only,3000,6600.000000,8,2592000.000000,1,COMPLETED_SUCCESSFULLY,6600.000000,3000.000000,9600.000000,0.000000,3000.000000,1.000000,1,580800.000000,"" +3:s2,fb_user_think_time_only,3000,7800.000000,8,2592000.000000,1,COMPLETED_SUCCESSFULLY,7800.000000,3000.000000,10800.000000,0.000000,3000.000000,1.000000,1,580800.000000,"" +2:s2,fb_user_think_time_only,4800,6000.000000,8,2592000.000000,1,COMPLETED_SUCCESSFULLY,6000.000000,4800.000000,10800.000000,0.000000,4800.000000,1.000000,0,971400.000000,"" diff --git a/test/test_fb_users.py b/test/test_fb_users.py index 160884efb8cc273fa4d605c22dd8f6bab87b9532..f8067da7a60d103a6cfcbb3d0bf82c6e3b2595b1 100644 --- a/test/test_fb_users.py +++ b/test/test_fb_users.py @@ -87,6 +87,13 @@ def test_tt_only_fully_loaded_platform(): test_input='load_platform') +def test_simultaneous_active_sessions(): + """Test when there are 2 simulataneous active sessions for one user. + See issue#8: https://gitlab.irit.fr/sepia-pub/mael/batmen/-/issues/8""" + launch_fb_test_1user(user_category='fb_user_think_time_only', + test_input='simultaneous_active_sessions') + + ##### Tests integration ##### def test_tt_only_simple_workload(): """A simple SAB json. See diagram https://app.diagrams.net/#G1tbo7oHahsgxTmhICucCGam5XNtshOUOb""" diff --git a/test/workloads/SABjson/simultaneous_active_sessions.SABjson b/test/workloads/SABjson/simultaneous_active_sessions.SABjson new file mode 100644 index 0000000000000000000000000000000000000000..a5e9e7716286bdbad1b2f4e6e8c26bc50b46a49e --- /dev/null +++ b/test/workloads/SABjson/simultaneous_active_sessions.SABjson @@ -0,0 +1,73 @@ +{ + "description": "Example highlighting the possibility of having several simultaneous active sessions. See diagram at https://gitlab.irit.fr/sepia-pub/mael/batmen/-/issues/8#note_11372", + "command": "Manually created. Coherent with arrival delim, threshold = 60mn", + "nb_res": 8, + "sessions": [ + { + "id": 1, + "first_submit_time": 0.0, + "preceding_sessions": [], + "thinking_time_after_preceding_session": [], + "nb_jobs": 1, + "jobs": [ + { + "id": 1, + "profile": "2400", + "res": 8, + "subtime": 0.0, + "walltime": 2592000.0 + } + ] + }, + { + "id": 2, + "first_submit_time": 6000.0, + "preceding_sessions": [], + "thinking_time_after_preceding_session": [], + "nb_jobs": 2, + "jobs": [ + { + "id": 2, + "profile": "4800", + "res": 8, + "subtime": 0.0, + "walltime": 2592000.0 + }, + { + "id": 3, + "profile": "3000", + "res": 8, + "subtime": 1800.0, + "walltime": 2592000.0 + } + ] + }, + { + "id": 3, + "first_submit_time": 14400.0, + "preceding_sessions": [ + 1 + ], + "thinking_time_after_preceding_session": [ + 2400.0 + ], + "nb_jobs": 2, + "jobs": [ + { + "id": 4, + "profile": "4800", + "res": 8, + "subtime": 0.0, + "walltime": 2592000.0 + }, + { + "id": 5, + "profile": "3000", + "res": 8, + "subtime": 1800.0, + "walltime": 2592000.0 + } + ] + } + ] +} \ No newline at end of file