@@ -37,6 +37,41 @@ class InvalidRequestID(Exception):
37
37
pass
38
38
39
39
40
+ class QoSRule (BaseModel ):
41
+ """QoS Rule ORM model."""
42
+
43
+ __tablename__ = "qos_rules"
44
+
45
+ uid = sa .Column (sa .Text , primary_key = True )
46
+ name = sa .Column (sa .Text )
47
+ info = sa .Column (sa .Text )
48
+ condition = sa .Column (sa .Text )
49
+ conclusion = sa .Column (sa .Text )
50
+ conclusion_value = sa .Column (sa .Text )
51
+ queued = sa .Column (sa .Integer )
52
+ running = sa .Column (sa .Integer )
53
+
54
+ system_requests : sa .orm .Mapped [list ["SystemRequest" ]] = sa .orm .relationship (
55
+ "SystemRequest" ,
56
+ secondary = "system_request_qos_rule" ,
57
+ back_populates = "qos_rules" ,
58
+ uselist = True ,
59
+ )
60
+
61
+
62
+ class SystemRequestQoSRule (BaseModel ):
63
+ """Association table for SystemRequest and QoSRule."""
64
+
65
+ __tablename__ = "system_request_qos_rule"
66
+
67
+ request_uid = sa .Column (
68
+ sa .dialects .postgresql .UUID (False ),
69
+ sa .ForeignKey ("system_requests.request_uid" ),
70
+ primary_key = True ,
71
+ )
72
+ rule_uid = sa .Column (sa .Text , sa .ForeignKey ("qos_rules.uid" ), primary_key = True )
73
+
74
+
40
75
class Events (BaseModel ):
41
76
"""Events ORM model."""
42
77
@@ -102,9 +137,21 @@ class SystemRequest(BaseModel):
102
137
__mapper_args__ = {"eager_defaults" : False }
103
138
104
139
# joined is temporary
105
- cache_entry = sa .orm .relationship (cacholote .database .CacheEntry , lazy = "joined" )
106
- adaptor_properties = sa .orm .relationship (AdaptorProperties , lazy = "select" )
107
- events = sa .orm .relationship (Events , lazy = "select" , passive_deletes = True )
140
+ cache_entry : sa .orm .Mapped ["cacholote.database.CacheEntry" ] = sa .orm .relationship (
141
+ cacholote .database .CacheEntry , lazy = "joined"
142
+ )
143
+ adaptor_properties : sa .orm .Mapped ["AdaptorProperties" ] = sa .orm .relationship (
144
+ AdaptorProperties , lazy = "select"
145
+ )
146
+ events : sa .orm .Mapped [list ["Events" ]] = sa .orm .relationship (
147
+ Events , lazy = "select" , passive_deletes = True
148
+ )
149
+ qos_rules : sa .orm .Mapped [list ["QoSRule" ]] = sa .orm .relationship (
150
+ "QoSRule" ,
151
+ secondary = "system_request_qos_rule" ,
152
+ back_populates = "system_requests" ,
153
+ uselist = True ,
154
+ )
108
155
109
156
@property
110
157
def age (self ):
@@ -376,40 +423,108 @@ def get_events_from_request(
376
423
return events
377
424
378
425
379
- def get_qos_status_from_request (
380
- request : SystemRequest ,
381
- ) -> dict [str , list [tuple [str , str ]]]:
382
- ret_value : dict [str , list [str ]] = {}
383
- for rule_name , rules in request .qos_status .items ():
384
- ret_value [rule_name ] = []
385
- for rule in rules .values ():
386
- ret_value [rule_name ].append (
387
- (rule .get ("info" , "" ), rule .get ("conclusion" , "" ))
388
- )
389
- return ret_value
426
+ def reset_qos_rules (session : sa .orm .Session ):
427
+ """Delete all QoS rules."""
428
+ for rule in session .scalars (sa .select (QoSRule )):
429
+ rule .system_requests = []
430
+ session .delete (rule )
431
+ session .commit ()
390
432
391
433
392
- def set_request_qos_rule (
393
- request : SystemRequest ,
434
+ def get_qos_rule (uid : str , session : sa .orm .Session ):
435
+ """Get a QoS rule."""
436
+ statement = sa .select (QoSRule ).where (QoSRule .uid == uid )
437
+ return session .scalars (statement ).one ()
438
+
439
+
440
+ def add_qos_rule (
394
441
rule ,
395
442
session : sa .orm .Session ,
443
+ queued : int = 0 ,
444
+ running : int = 0 ,
396
445
):
397
- qos_status = request .qos_status
398
- old_rules = qos_status .get (rule .name , {})
399
- rule_uid = rule .get_uid (request )
400
- if rule_uid in old_rules :
401
- return
402
- old_rules [rule_uid ] = {
403
- "conclusion" : str (rule .evaluate (request )),
404
- "info" : str (rule .info ).replace ('"' , "" ),
405
- "condition" : str (rule .condition ),
406
- }
407
- qos_status [rule .name ] = old_rules
408
- session .execute (
409
- sa .update (SystemRequest )
410
- .filter_by (request_uid = request .request_uid )
411
- .values (qos_status = qos_status )
446
+ """Add a QoS rule."""
447
+ qos_rule = QoSRule (
448
+ uid = str (rule .__hash__ ()),
449
+ name = str (rule .name ),
450
+ info = str (rule .info ),
451
+ condition = str (rule .condition ),
452
+ conclusion = str (rule .conclusion ),
453
+ # conclusion_value may change over time, this case is not handled
454
+ conclusion_value = str (rule .evaluate (request = None )),
455
+ queued = queued ,
456
+ running = running ,
412
457
)
458
+ session .add (qos_rule )
459
+ session .commit ()
460
+ return qos_rule
461
+
462
+
463
+ def increment_qos_rule_running (rules : list , session : sa .orm .Session ):
464
+ """Increment the running counter of a QoS rule."""
465
+ for rule in rules :
466
+ try :
467
+ qos_rule = get_qos_rule (str (rule .__hash__ ()), session )
468
+ except sqlalchemy .orm .exc .NoResultFound :
469
+ qos_rule = add_qos_rule (rule = rule , session = session )
470
+ qos_rule .running += 1
471
+ session .commit ()
472
+
473
+
474
+ def decrement_qos_rule_running (rules : list , session : sa .orm .Session ):
475
+ """Increment the running counter of a QoS rule."""
476
+ for rule in rules :
477
+ qos_rule = get_qos_rule (str (rule .__hash__ ()), session )
478
+ qos_rule .running -= 1
479
+ session .commit ()
480
+
481
+
482
+ def delete_request_qos_status (request_uid : str , rules : list , session : sa .orm .Session ):
483
+ """Delete all QoS rules from a request."""
484
+ request = get_request (request_uid , session )
485
+ for rule in rules :
486
+ try :
487
+ qos_rule = get_qos_rule (str (rule .__hash__ ()), session )
488
+ except sqlalchemy .orm .exc .NoResultFound :
489
+ qos_rule = add_qos_rule (rule = rule , session = session )
490
+ if qos_rule in request .qos_rules :
491
+ request .qos_rules .remove (qos_rule )
492
+ qos_rule .queued -= 1
493
+ qos_rule .running += 1
494
+ session .commit ()
495
+
496
+
497
+ def add_request_qos_status (request_uid : str , rules : list , session : sa .orm .Session ):
498
+ request = get_request (request_uid , session )
499
+ for rule in rules :
500
+ try :
501
+ qos_rule = get_qos_rule (str (rule .__hash__ ()), session )
502
+ except sqlalchemy .orm .exc .NoResultFound :
503
+ qos_rule = add_qos_rule (rule = rule , session = session )
504
+ if qos_rule not in request .qos_rules :
505
+ qos_rule .queued += 1
506
+ request .qos_rules .append (qos_rule )
507
+ session .commit ()
508
+
509
+
510
+ def get_qos_status_from_request (
511
+ request : SystemRequest ,
512
+ ) -> dict [str , list [dict [str , str ]]]:
513
+ ret_value : dict [str , list [dict [str , str ]]] = {}
514
+ rules = request .qos_rules
515
+ for rule in rules :
516
+ rule_name = rule .name
517
+ rule_summary = {
518
+ "info" : rule .info ,
519
+ "queued" : rule .queued ,
520
+ "running" : rule .running ,
521
+ "conclusion" : rule .conclusion_value ,
522
+ }
523
+ if rule_name not in ret_value :
524
+ ret_value [rule_name ] = [rule_summary ]
525
+ else :
526
+ ret_value [rule_name ].append (rule_summary )
527
+ return ret_value
413
528
414
529
415
530
def requeue_request (
@@ -428,7 +543,7 @@ def requeue_request(
428
543
request .request_metadata = metadata
429
544
request .status = "accepted"
430
545
session .commit ()
431
- logger .info ("--------- requeueing request" , ** logger_kwargs (request = request ))
546
+ logger .info ("requeueing request" , ** logger_kwargs (request = request ))
432
547
return request
433
548
else :
434
549
return
0 commit comments