Add 'PostgreSQL/uuid-index-maintenance.sql'
This commit is contained in:
parent
8da8728bd2
commit
e41967f871
205
PostgreSQL/uuid-index-maintenance.sql
Normal file
205
PostgreSQL/uuid-index-maintenance.sql
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Author: DBRE - Joe Smith
|
||||||
|
* Date: Sep 2022
|
||||||
|
* Purpose: sets up tables, functions and extensions for index maintenance when using uuids.
|
||||||
|
*/
|
||||||
|
|
||||||
|
-- required to schedule job
|
||||||
|
create extension pg_cron;
|
||||||
|
|
||||||
|
-- required to calculate bloat
|
||||||
|
create extension pgstattuple;
|
||||||
|
|
||||||
|
-- table defines indexes to maintain, target bloat percentage which triggers maintenance, and an acceptable window to
|
||||||
|
-- do maintenance if the bloat is met during hours we dont want to run the maintenance
|
||||||
|
CREATE TABLE index_maintenance_configuration (
|
||||||
|
id int primary key,
|
||||||
|
index_name varchar(512),
|
||||||
|
maintenance_threshold_percent float,
|
||||||
|
man_window_start timestamp,
|
||||||
|
man_window_end timestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
-- table tracks number of times maintenance is done.
|
||||||
|
-- when maintenance was done
|
||||||
|
CREATE TABLE index_maintenance (
|
||||||
|
index_name varchar(512),
|
||||||
|
index_iteration bigint,
|
||||||
|
maintenance_done timestamp,
|
||||||
|
scheduled boolean
|
||||||
|
PRIMARY KEY(index_name, index_iteration, scheduled)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- no maintenance if you don't have a config
|
||||||
|
ALTER TABLE index_maintenance
|
||||||
|
ADD FOREIGN KEY (index_name)
|
||||||
|
REFERENCES index_maintenance_configuration(index_name);
|
||||||
|
|
||||||
|
-- returns bloat as float for an index.
|
||||||
|
create or replace function return_idx_bloat (idx varchar(512))
|
||||||
|
language plpgsql
|
||||||
|
as
|
||||||
|
$$
|
||||||
|
RETURNS float AS $$
|
||||||
|
declare
|
||||||
|
current_bloat float;
|
||||||
|
|
||||||
|
begin
|
||||||
|
SELECT 100-(pgstatindex(idx)).avg_leaf_density into current_bloat;
|
||||||
|
return current_bloat;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
-- calls the re-index function, concurrently.
|
||||||
|
-- raises a notice in logs which can be parsed from cloud watch
|
||||||
|
-- guarantees date is between dates, casts then to timestamps.
|
||||||
|
create or replace function call_reindex(ind varchar(512), man_window_start timestamp,
|
||||||
|
man_window_end timestamp)
|
||||||
|
language plpgsql
|
||||||
|
as
|
||||||
|
$$
|
||||||
|
RETURNS boolean AS $$
|
||||||
|
declare
|
||||||
|
today timestamp;
|
||||||
|
new_index bigint;
|
||||||
|
begin
|
||||||
|
today = now();
|
||||||
|
|
||||||
|
if not man_window_start then;
|
||||||
|
-- see call_reindex function
|
||||||
|
RAISE NOTICE 'call_reindex(%, no_man_window_specified)', idx;
|
||||||
|
REINDEX CONCURRENTLY idx;
|
||||||
|
return true;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- if the time of the run is within the right window, we re-index.
|
||||||
|
-- we return the same iteration to close out that record.
|
||||||
|
if today::timestamp between man_window_start::timestamp and man_window_end::timestamp;
|
||||||
|
RAISE NOTICE 'call_reindex(%, %)', idx, today;
|
||||||
|
REINDEX CONCURRENTLY idx;
|
||||||
|
return true;
|
||||||
|
else;
|
||||||
|
return false;
|
||||||
|
end if;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
|
||||||
|
create or replace function do_idx_maintenance(idx varchar(512))
|
||||||
|
language plpgsql
|
||||||
|
as
|
||||||
|
$$
|
||||||
|
RETURNS RECORD AS $$
|
||||||
|
declare
|
||||||
|
current_iteration bigint;
|
||||||
|
new_iteration bigint;
|
||||||
|
is_scheduled boolean;
|
||||||
|
man_window_start timestamp;
|
||||||
|
man_window_end timestamp;
|
||||||
|
maintenance_threshold_percent float;
|
||||||
|
current_bloat float;
|
||||||
|
maintenance_ran boolean;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
-- look for configuration for current ind
|
||||||
|
maintenance_threshold_percent, man_window_start, man_window_end = select maintenance_threshold_percent,
|
||||||
|
man_window_start. man_window_end
|
||||||
|
from index_maintenance_configuration
|
||||||
|
where index_name = idx;
|
||||||
|
|
||||||
|
-- check for a current iteration to increment, if none, then we start one at 0.
|
||||||
|
SELECT max(index_iteration), scheduled INTO current_iteration, is_scheduled FROM index_maintenance WHERE index_name = idx;
|
||||||
|
|
||||||
|
-- set iteration of current record if one does not exist.
|
||||||
|
-- other wise, we use the current_iteration from the table.
|
||||||
|
if not current_iteration then;
|
||||||
|
current_iteration = 0;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- no maintenance window is allowed and will reindex each time.
|
||||||
|
if not man_window_start then;
|
||||||
|
-- see call_reindex function
|
||||||
|
maintenance_ran = call_reindex(idx, man_window_start, man_window_end);
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- if the row we get back is scheduled, we try to run it.
|
||||||
|
if is_scheduled;
|
||||||
|
|
||||||
|
maintenance_ran = call_reindex(idx, man_window_start, man_window_end);
|
||||||
|
|
||||||
|
|
||||||
|
else;
|
||||||
|
-- here we have no row that is scheduled.
|
||||||
|
-- we have a maintenance window to check.
|
||||||
|
-- we check the current bloat, and try to re-index.
|
||||||
|
current_bloat = return_idx_bloat(idx);
|
||||||
|
|
||||||
|
if current_bloat > maintenance_threshold_percent then;
|
||||||
|
maintenance_ran = call_reindex(idx, man_window_start, man_window_end);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- using simple logic, we will either increment the maintenance row, or create a new one.
|
||||||
|
return maintenance_ran, current_iteration;
|
||||||
|
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
create or replace function uuid_idx_maintenance()
|
||||||
|
language plpgsql
|
||||||
|
as
|
||||||
|
$$
|
||||||
|
declare
|
||||||
|
idx_array varchar[];
|
||||||
|
|
||||||
|
begin
|
||||||
|
-- return all indexes we are to maintain, if none, we do nothing.
|
||||||
|
select array_agg( distinct idx_name )
|
||||||
|
into idx_array
|
||||||
|
from index_maintenance_configuration;
|
||||||
|
|
||||||
|
-- if there are no indexes, its a no op.
|
||||||
|
if array_length(idx_array) > 0 then;
|
||||||
|
|
||||||
|
-- iterate over each name in array
|
||||||
|
foreach idx in array idx_array loop
|
||||||
|
|
||||||
|
-- define variable for current iteration
|
||||||
|
maintenance_done boolean;
|
||||||
|
current_iteration bigint;
|
||||||
|
|
||||||
|
rec record;
|
||||||
|
|
||||||
|
-- call function to check idx, return record with multiple results.
|
||||||
|
-- parse out information into variables.
|
||||||
|
|
||||||
|
rec = do_idx_maintenance(idx);
|
||||||
|
maintenance_done = rec[0]
|
||||||
|
current_iteration = rec[1]
|
||||||
|
|
||||||
|
-- maintenance_done coming back true means the maintenance was done.
|
||||||
|
-- maintenance_done coming back false means that there was no maintenance done.
|
||||||
|
if maintenance_done then;
|
||||||
|
UPDATE set maintenance_done = now(), scheduled = false
|
||||||
|
WHERE index_name = idx (idx, current_iteration, now())
|
||||||
|
AND index_iteration = current_iteration;
|
||||||
|
else;
|
||||||
|
-- this record will violate a primary key if a row already exists.
|
||||||
|
-- otherwise, it schedules a row for the next run to review.
|
||||||
|
INSERT into
|
||||||
|
index_maintenance(index_name, index_iteration, maintenance_done, scheduled )
|
||||||
|
VALUES (idx, current_iteration,null, true) on conflict do nothing;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
|
end loop;
|
||||||
|
else;
|
||||||
|
return
|
||||||
|
end if;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user