- Posted by Gavin Soorma
- On June 14, 2019
- 0 Comments
With Oracle Resource Manager, we had a way to limit and regulate use of resources like CPU and I/O as well as we had the ability to prevent the execution of any long running queries which exceeded a defined threshold.
So we could ‘cancel’ or terminate a SQL query which was running longer than a defined threshold of say 10 minutes.
All that was good – but nothing prevented that same query from being executed again and again running each time for 10 minutes and only then getting terminated – wasting 10 minutes of resources each time it was executed.
New in Oracle 19c is the new concept of SQL Quarantine where if a particular SQL statement exceeds the specified resource limit (set via Oracle Resource Manager), then the Resource Manager terminates the execution of that statement and “quarantines” the plan.
This broadly speaking means that the execution plan is now placed on a “blacklist” of plans that the database will not execute.
This SQL Quarantine feature in turn helps the performance as it prevents the future execution of a costly SQL statement which has now been quarantined.
However note: As we speak, this feature in only available on Oracle Engineered Systems (both on-premise as well as ExaCS) and to test this out I had to set this underscore parameter and bounce the database:
alter system set “_exadata_feature_on”=true scope=spfile;
Let us have a quick look at how this feature works.
We begin by creating a consumer group and resource plan and then adding a plan directive which will limit the run time of queries executed by the DEMO schema to 20 seconds. Note that the plan directive was initially set to 20 seconds of CPU time and not wall clock elapsed time and it was altered to specify the threshold based on elapsed time instead.
So this part of creating and configuring a database resource plan is pretty standard and no changes required here.
begin dbms_resource_manager.create_pending_area(); dbms_resource_manager.create_consumer_group( CONSUMER_GROUP=>'GROUP_WITH_LIMITED_EXEC_TIME', COMMENT=>'This is the consumer group that has limited execution time per statement' ); dbms_resource_manager.set_consumer_group_mapping( attribute => 'ORACLE_USER', value => 'DEMO', consumer_group =>'GROUP_WITH_LIMITED_EXEC_TIME' ); dbms_resource_manager.create_plan( PLAN=> 'LIMIT_EXEC_TIME', COMMENT=>'Kill statement after exceeding total execution time' ); dbms_resource_manager.create_plan_directive( PLAN=> 'LIMIT_EXEC_TIME', GROUP_OR_SUBPLAN=>'GROUP_WITH_LIMITED_EXEC_TIME', COMMENT=>'Kill statement after exceeding total execution time', SWITCH_GROUP=>'CANCEL_SQL', SWITCH_TIME=>30, SWITCH_ESTIMATE=>false ); dbms_resource_manager.create_plan_directive( PLAN=> 'LIMIT_EXEC_TIME', GROUP_OR_SUBPLAN=>'OTHER_GROUPS', COMMENT=>'leave others alone', CPU_P1=>100 ); DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA; DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA; end; / begin dbms_resource_manager.create_pending_area(); dbms_resource_manager_privs.grant_switch_consumer_group('DEMO','GROUP_WITH_LIMITED_EXEC_TIME',false); dbms_resource_manager.set_initial_consumer_group('DEMO','GROUP_WITH_LIMITED_EXEC_TIME'); DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA; DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA; end; / BEGIN dbms_resource_manager.update_plan_directive(plan=>'LIMIT_EXEC_TIME', group_or_subplan=>'GROUP_WITH_LIMITED_EXEC_TIME',new_switch_elapsed_time=>20, new_switch_for_call=>TRUE,new_switch_group=>'CANCEL_SQL' ); dbms_resource_manager.validate_pending_area(); dbms_resource_manager.submit_pending_area; END; /
Let us test the resource plan.
We connect as the DEMO user and issue a query which will exceed the elapsed time threshold of 20 seconds defined in the resource plan.
We will see an error message and the running query will be terminated.
ERROR: ORA-56735: elapsed time limit exceeded - call aborted 300 rows selected. Elapsed: 00:00:19.64 SQL>
We use the DBMS_SQLQ package to create a quarantine configuration for an execution plan of a SQL statement which needs to be quarantined. We can create a quarantine configuration either by specifying the SQL text or the SQL_ID of the statement to be quarantined – CREATE_QUARANTINE_BY_SQL_ID or CREATE_QUARANTINE_BY_SQL_TEXT.
DECLARE quarantine_config VARCHAR2(30); BEGIN quarantine_config := DBMS_SQLQ.CREATE_QUARANTINE_BY_SQL_ID(SQL_ID => '491fa2p6qt9h6'); END; /
After the quarantine configuration has been created for an execution plan for a SQL statement, we then specify quarantine thresholds using the DBMS_SQLQ.ALTER_QUARANTINE procedure.
When any of the thresholds defined by Resource Manager is equal to or less than a quarantine threshold specified in the SQL quarantine configuration, then the SQL statement is not allowed to run provided it uses the same execution plan specified in the quarantine configuration.
Note: the quarantine name can be obtained from DBA_SQL_QUARANTINE dictionary table.
BEGIN DBMS_SQLQ.ALTER_QUARANTINE( QUARANTINE_NAME => 'SQL_QUARANTINE_ca0z7uh2sqcbw', PARAMETER_NAME => 'ELAPSED_TIME', PARAMETER_VALUE => '30'); END; /
With the SQL Quarantine now in place, when we try and issue the same SQL statement (which earlier used to run for 20 seconds before being terminated by Resource Manager) it no longer gets executed and does not even start and we see the message stating that the plan being used for this statement is part of a quarantined plan.
SQL> set timing on SQL> select * from demo.myobjects where owner='SYS'; select * from demo.myobjects where owner='SYS' * ERROR at line 1: ORA-56955: quarantined plan used
The V$SQL view has two additional columns which show the name of the quarantine and how many executions of the SQL statement has been avoided by the quarantine now in place.
SQL> select sql_quarantine,avoided_executions 2 from v$sql where sql_id='491fa2p6qt9h6'; SQL_QUARANTINE -------------------------------------------------------------------------------- AVOIDED_EXECUTIONS ------------------ SQL_QUARANTINE_ca0z7uh2sqcbw 1
Using the DBMS_SQLQ package subprograms we can also enable or disable a quarantine configuration, delete a quarantine configuration and if required also transfer quarantine configurations from one database to another.
SQL> BEGIN DBMS_SQLQ.ALTER_QUARANTINE( QUARANTINE_NAME => 'SQL_QUARANTINE_ca0z7uh2sqcbw', PARAMETER_NAME => 'ENABLED', PARAMETER_VALUE => 'NO'); END; / PL/SQL procedure successfully completed.
Note that now since the quarantine has been disabled, the query is not prevented from immediate execution but is cancelled once the Resource Manager plan directive related to elapsed time takes effect.
ERROR: ORA-56735: elapsed time limit exceeded - call aborted 300 rows selected. Elapsed: 00:00:19.64