From a393b6105570d3ec2139cd7eaeafe504f95a1a34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Madon?= <mael.madon@irit.fr>
Date: Wed, 1 Mar 2023 12:13:58 +0100
Subject: [PATCH 1/4] test: example file for issue#8

---
 .../simultaneous_active_sessions.SABjson      | 73 +++++++++++++++++++
 1 file changed, 73 insertions(+)
 create mode 100644 test/workloads/SABjson/simultaneous_active_sessions.SABjson

diff --git a/test/workloads/SABjson/simultaneous_active_sessions.SABjson b/test/workloads/SABjson/simultaneous_active_sessions.SABjson
new file mode 100644
index 0000000..a5e9e77
--- /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
-- 
GitLab


From cb516723d14fee2cd4fcf140529775634cfe085c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Madon?= <mael.madon@irit.fr>
Date: Wed, 1 Mar 2023 12:32:51 +0100
Subject: [PATCH 2/4] test: added the test for issue#8

---
 test/test_fb_users.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/test/test_fb_users.py b/test/test_fb_users.py
index 160884e..f8067da 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"""
-- 
GitLab


From fb4ba1774728011e63f1e1d476b56e324deeadfd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Madon?= <mael.madon@irit.fr>
Date: Wed, 1 Mar 2023 16:21:24 +0100
Subject: [PATCH 3/4] dev: changes to allow multiple simulatneous active
 sessions, issue#8

---
 src/users/user_feedback.cpp | 143 +++++++++++++++++++++---------------
 src/users/user_feedback.hpp |  10 ++-
 2 files changed, 90 insertions(+), 63 deletions(-)

diff --git a/src/users/user_feedback.cpp b/src/users/user_feedback.cpp
index b4fb6bc..2d539f9 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 b5565d9..4a60ae0 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 &param);
 
+    /**
+     * 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 */
-- 
GitLab


From 12de3de9ce0bc27608622bbdf5a1f69d506e64bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Madon?= <mael.madon@irit.fr>
Date: Wed, 1 Mar 2023 16:21:53 +0100
Subject: [PATCH 4/4] test: related expected test to issue#8

---
 ...er_think_time_only-simultaneous_active_sessions_jobs.csv | 6 ++++++
 1 file changed, 6 insertions(+)
 create mode 100644 test/expected_log/fb_user_think_time_only-simultaneous_active_sessions_jobs.csv

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 0000000..df25af8
--- /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,""
-- 
GitLab