Skip to content
Snippets Groups Projects
Commit 263e254c authored by Jorrit Schaap's avatar Jorrit Schaap
Browse files

Task #10898: allow claimed claims to be shifted in time. only go to conflict...

Task #10898: allow claimed claims to be shifted in time. only go to conflict when task status <= queued.
parent beec5df1
No related branches found
No related tags found
No related merge requests found
...@@ -521,6 +521,7 @@ BEGIN ...@@ -521,6 +521,7 @@ BEGIN
--get all overlapping_claims, check whether they cause a conflict or not. --get all overlapping_claims, check whether they cause a conflict or not.
SET LOCAL client_min_messages=warning; --prevent "table overlapping_claims does not exist, skipping" message SET LOCAL client_min_messages=warning; --prevent "table overlapping_claims does not exist, skipping" message
DROP TABLE IF EXISTS overlapping_claims; -- TODO: use CREATE TEMPORARY TABLE IF NOT EXISTS when we will use postgres 9.5+ DROP TABLE IF EXISTS overlapping_claims; -- TODO: use CREATE TEMPORARY TABLE IF NOT EXISTS when we will use postgres 9.5+
SET LOCAL client_min_messages=info; --back to normal log level
CREATE TEMPORARY TABLE overlapping_claims CREATE TEMPORARY TABLE overlapping_claims
ON COMMIT DROP ON COMMIT DROP
AS SELECT * FROM resource_allocation.resource_claim rc AS SELECT * FROM resource_allocation.resource_claim rc
...@@ -608,6 +609,9 @@ DECLARE ...@@ -608,6 +609,9 @@ DECLARE
claim_conflict_status_id int := 2; --beware: hard coded instead of lookup for performance claim_conflict_status_id int := 2; --beware: hard coded instead of lookup for performance
task_approved_status_id int := 300; --beware: hard coded instead of lookup for performance task_approved_status_id int := 300; --beware: hard coded instead of lookup for performance
task_conflict_status_id int := 335; --beware: hard coded instead of lookup for performance task_conflict_status_id int := 335; --beware: hard coded instead of lookup for performance
task_prescheduled_status_id int := 350; --beware: hard coded instead of lookup for performance
task_scheduled_status_id int := 400; --beware: hard coded instead of lookup for performance
task_queued_status_id int := 500; --beware: hard coded instead of lookup for performance
claim_has_conflicts boolean; claim_has_conflicts boolean;
BEGIN BEGIN
--order of following steps is important, do not reorder the steps --order of following steps is important, do not reorder the steps
...@@ -621,34 +625,41 @@ BEGIN ...@@ -621,34 +625,41 @@ BEGIN
IF TG_OP = 'UPDATE' THEN IF TG_OP = 'UPDATE' THEN
-- bounce any updated claim which has conflict state, but is tried to be updated to claimed -- bounce any updated claim which has conflict state, but is tried to be updated to claimed
-- only this function can 'reset' the conflict state back to tentative!
IF NEW.status_id = claim_claimed_status_id AND OLD.status_id = claim_conflict_status_id THEN IF NEW.status_id = claim_claimed_status_id AND OLD.status_id = claim_conflict_status_id THEN
RAISE EXCEPTION 'cannot update claim-in-conflict to status claimed; old:% new:%', OLD, NEW; RAISE EXCEPTION 'cannot update claim-in-conflict to status claimed; old:% new:%', OLD, NEW;
END IF; END IF;
-- bounce any updated claim which has claimed state onto which other properties are changed -- bounce any claim_size updates on claimed claims
IF NEW.status_id = claim_claimed_status_id AND OLD.status_id = NEW.status_id THEN IF NEW.status_id = claim_claimed_status_id AND OLD.claim_size <> NEW.claim_size THEN
RAISE EXCEPTION 'updates on properties of claimed claim are not allowed; old:% new:%', OLD, NEW; RAISE EXCEPTION 'cannot update claim size on claimed claim; old:% new:%', OLD, NEW;
END IF; END IF;
END IF; END IF;
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN --only check claim if status and/or claim_size and/or start/end time changed
--only check claim if in tentative/conflict status, and status or claim_size or start/end time changed IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND (OLD.status_id <> NEW.status_id OR
IF NEW.status_id = claim_tentative_status_id OR NEW.status_id = claim_conflict_status_id THEN OLD.claim_size <> NEW.claim_size OR
IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND (OLD.status_id <> NEW.status_id OR OLD.starttime <> NEW.starttime OR
OLD.claim_size <> NEW.claim_size OR OLD.endtime <> NEW.endtime)) THEN
OLD.starttime <> NEW.starttime OR --check if claim fits or has conflicts
OLD.endtime <> NEW.endtime)) THEN SELECT * FROM resource_allocation.has_conflict_with_overlapping_claims(NEW) INTO claim_has_conflicts;
--check if claim fits or has conflicts
SELECT * FROM resource_allocation.has_conflict_with_overlapping_claims(NEW) INTO claim_has_conflicts; IF claim_has_conflicts THEN
IF NEW.status_id <> claim_conflict_status_id THEN
IF claim_has_conflicts AND NEW.status_id <> claim_conflict_status_id THEN -- only set claims to conflict if task status <= queued
-- when a claim goes to conflict, then so does it's task, and we don't want that for running/finished/aborted tasks
IF EXISTS (SELECT 1 FROM resource_allocation.task
WHERE id=NEW.task_id
AND status_id = ANY(ARRAY[300, 335, 350, 400, 500])) THEN -- hardcoded tasks statuses <= queued
-- conflict with others, so set claim status to conflict -- conflict with others, so set claim status to conflict
NEW.status_id := claim_conflict_status_id; NEW.status_id := claim_conflict_status_id;
ELSIF NOT claim_has_conflicts AND NEW.status_id <> claim_tentative_status_id THEN
-- no conflict (anymore) with others, so set claim status to tentative
NEW.status_id := claim_tentative_status_id;
END IF; END IF;
END IF; END IF;
ELSE
-- no conflict (anymore) with others, so set claim status to tentative if currently in conflict
IF NEW.status_id = claim_conflict_status_id THEN
NEW.status_id := claim_tentative_status_id;
END IF;
END IF; END IF;
END IF; END IF;
......
...@@ -1148,6 +1148,44 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): ...@@ -1148,6 +1148,44 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase):
self.assertEqual(0, len(self.radb.get_conflicting_overlapping_claims(t2_claim_ids[0]))) self.assertEqual(0, len(self.radb.get_conflicting_overlapping_claims(t2_claim_ids[0])))
self.assertEqual(0, len(self.radb.get_conflicting_overlapping_tasks(t2_claim_ids[0]))) self.assertEqual(0, len(self.radb.get_conflicting_overlapping_tasks(t2_claim_ids[0])))
# updating task/claim start/endtime should work, even for scheduled tasks with claimed claims
# effect might be that a scheduled tasks goes to conflict
# force conflict by moving back to original start/endtimes
self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=task2['starttime'], endtime=task2['endtime']))
self.assertEqual('conflict', self.radb.getResourceClaim(t2_claim_ids[0])['status'])
self.assertEqual('conflict', self.radb.getTask(task_id2)['status'])
# again do conflict resolution, shift task and claims
self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=now+timedelta(hours=2), endtime=now+timedelta(hours=3)))
self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, claim_status='claimed', task_status='scheduled'))
# now the task and claim status should be scheduled/claimed
self.assertEqual('scheduled', self.radb.getTask(task_id2)['status'])
self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status'])
# updating task/claim start/endtime should work, even for scheduled tasks with claimed claims
# effect might be that a scheduled tasks goes to conflict
# now, make simple endtime adjustment, task should stay scheduled
self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, endtime=now+timedelta(hours=2.75)))
# now the task and claim status should still be scheduled/claimed
self.assertEqual('scheduled', self.radb.getTask(task_id2)['status'])
self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status'])
# now some weird corner case...
# when a task is > queued (for example, finished)
# then we don't want conflict statuses anymore if we update start/endtimes
# test here with weird starttime shift back to overlap with task1
self.assertTrue(self.radb.updateTask(task_id2, task_status='finished'))
self.assertEqual('finished', self.radb.getTask(task_id2)['status'])
self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=task1['starttime']))
self.assertEqual('finished', self.radb.getTask(task_id2)['status'])
self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status'])
#ok, that works, now set the start/end time back to 'normal' for some later test cases
self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=now+timedelta(hours=2), endtime=now+timedelta(hours=3)))
self.assertEqual('finished', self.radb.getTask(task_id2)['status'])
self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status'])
logger.info('------------------------------ concludes task 2 ------------------------------') logger.info('------------------------------ concludes task 2 ------------------------------')
logger.info('-- now test with a 3rd task, and test resource availability, conflicts etc. --') logger.info('-- now test with a 3rd task, and test resource availability, conflicts etc. --')
...@@ -1233,6 +1271,7 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): ...@@ -1233,6 +1271,7 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase):
self.assertEqual('claimed', self.radb.getResourceClaim(t3_claim_ids[0])['status']) self.assertEqual('claimed', self.radb.getResourceClaim(t3_claim_ids[0])['status'])
self.assertEqual('scheduled', self.radb.getTask(task_id3)['status']) self.assertEqual('scheduled', self.radb.getTask(task_id3)['status'])
# suppose the resource_usages table is broken for some reason, fix it.... # suppose the resource_usages table is broken for some reason, fix it....
# break it first... # break it first...
self._execute_query('TRUNCATE TABLE resource_allocation.resource_usage;') self._execute_query('TRUNCATE TABLE resource_allocation.resource_usage;')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment