-
Notifications
You must be signed in to change notification settings - Fork 5
Use cases
Various tasks can be solved using the db_converter. This page covers the most simple and useful examples. More examples see in packets directory.
Prepare the packet directory:
cd db_converter
mkdir packets/create_userMake the packet with one object generator:
cat > packets/create_user/01_gen_obj.sql << EOL
SELECT '' as maint, 'md5' || md5('DBC_PL_PASSW' || 'DBC_PL_USER_NAME');
EOL
cat > packets/create_user/01_step.sql << EOL
CREATE USER DBC_PL_USER_NAME WITH ENCRYPTED PASSWORD 'GEN_OBJ_FLD_1';
EOL
cat > packets/create_user/02_step.sql << EOL
GRANT USAGE on SCHEMA public TO DBC_PL_USER_NAME;
ALTER DEFAULT PRIVILEGES FOR ROLE DBC_PL_USER_NAME IN SCHEMA public
GRANT SELECT ON TABLES TO DBC_PL_USER_NAME;
EOLRun packet and check output:
python3 db_converter.py --packet-name=create_user --db-name=some_db_* \
--placeholders='{"USER_NAME":"new_test_user","PASSW":"12345"}'To remove user execute:
ALTER DEFAULT PRIVILEGES FOR ROLE new_test_user IN SCHEMA public
REVOKE SELECT ON TABLES FROM new_test_user;
DROP USER new_test_user;Task: there are several databases with a large number of schemes, it is necessary to change the fillfactor for certain indexes
Prepare the packet directory:
cd db_converter
mkdir packets/set_fillfactor_idxMake a packet with one schema generator:
cat > set_fillfactor_idx/01_gen_nsp.sql << EOL
SELECT '' as maint, nspname
FROM pg_namespace n
WHERE nspname like 'client_%_n'
EOL
cat > set_fillfactor_idx/01_step.sql << EOL
ALTER INDEX GEN_NSP_FLD_1.tbl_fld_idx SET (fillfactor = 70);
EOLRun packet and check output:
python3 db_converter.py --packet-name=set_fillfactor_idx --db-name=some_db_*Prepare the packet directory:
cd db_converter
mkdir packets/alter_userMake a packet with one object generator:
cat > alter_user/01_gen_obj.sql << EOL
select '' as maint, 'support_read'
union
select '' as maint, 'support_write'
EOL
cat > alter_user/01_step.sql << EOL
ALTER USER GEN_OBJ_FLD_1 RESET ALL;
ALTER USER GEN_OBJ_FLD_1 set work_mem = '50MB';
ALTER USER GEN_OBJ_FLD_1 set maintenance_work_mem = '50MB';
ALTER USER GEN_OBJ_FLD_1 set max_parallel_workers_per_gather = 0;
ALTER USER GEN_OBJ_FLD_1 set statement_timeout = '10min';
ALTER USER GEN_OBJ_FLD_1 set idle_in_transaction_session_timeout = '10min';
ALTER USER GEN_OBJ_FLD_1 set lock_timeout = '5min';
EOLRun packet and check output:
python3 db_converter.py --packet-name=alter_user --db-name=some_db_*Task: delete data older than 6 months. The amount of data being deleted cannot be processed within a single transaction
Prepare the packet directory:
cd db_converter
mkdir packets/delete_old_dataMake the packet with one object generator:
-- a packet of this type can be redeployed
cat > delete_old_data/meta_data.json << EOL
{
"type": "maintenance",
"postgresql": {
"statement_timeout": "10min"
}
}
EOL
-- delete data older than 6 months in 1 week increments, one transaction deletes data in one week
-- NOTE: taking into account the uniform distribution of data over time
cat > delete_old_data/01_gen_obj.sql << EOL
with dates as (
select
(select date_trunc('d', min(inserted)) from some_tbl)::TIMESTAMP WITHOUT TIME ZONE as date_a,
(select date_trunc('d', now() - interval '6 month'))::TIMESTAMP WITHOUT TIME ZONE as date_b
)
select
(
case when T.id % 10 = 1 -- each 10 transactions run vacuum analyze
then 'vacuum analyze some_tbl'
end
) as maint, T.a, T.b from (
with grid_a as (
select T.dt_a as dt_a, row_number() OVER () as id
from (
SELECT generate_series(
(select date_a - ('1 week'::interval) from dates),
(select date_b from dates),
'1 week'::interval
) as dt_a
union SELECT date_b from dates
ORDER BY 1
) T
),
grid_b as (
select T.dt_b as dt_b, row_number() OVER () as id
from (
SELECT generate_series(
(select date_a from dates),
(select date_b from dates),
'1 week'::interval
) as dt_b
union SELECT date_b from dates
ORDER BY 1
) T
)
SELECT A.dt_a as a, B.dt_b as b, A.id from grid_a A
join grid_b B on A.id = B.id
) T;
EOL
cat > delete_old_data/01_step.sql << EOL
DELETE
FROM some_tbl
WHERE inserted >= 'GEN_OBJ_FLD_1' and inserted < 'GEN_OBJ_FLD_2'
EOLRun packet and check output:
python3 db_converter.py --packet-name=delete_old_data --db-name=some_db_*Task: Update all records in huge table. The amount of data being updated cannot be processed within a single transaction
Prepare the packet directory:
cd db_converter
mkdir packets/update_tableMake the packet with one object generator:
cat > update_table/01_gen_obj.sql << EOL
with pk_intervals as (
select
(select min(id) from some_table) as point_a,
(select max(id) from some_table) as point_b
)
select
(case when T.id % 1000 = 1 -- each 1K transactions run vacuum analyze
then 'vacuum analyze some_table' end
) as maint, -- "maint" is a system field
T.a, -- GEN_OBJ_FLD_1
T.b -- GEN_OBJ_FLD_2
from (
with grid_a as (
select T.point_a as point_a, row_number() OVER () as id
from (
SELECT generate_series(
(select point_a from pk_intervals),
(select point_b from pk_intervals) + 50000,
50000
) as point_a
ORDER BY 1
) T
),
grid_b as (
select T.point_b as point_b, row_number() OVER () as id
from (
SELECT generate_series(
(select point_a from pk_intervals) + 50000,
(select point_b from pk_intervals) + 50000,
50000
) as point_b
ORDER BY 1
) T
)
SELECT A.point_a as a, B.point_b as b, A.id from grid_a A
join grid_b B on A.id = B.id
) T;
EOL
cat > update_table/01_step.sql << EOL
UPDATE some_table SET id_new = id
WHERE id >= GEN_OBJ_FLD_1 and id <= GEN_OBJ_FLD_2
EOLRun packet and check output:
python3 db_converter.py --packet-name=update_table --db-name=some_db_*Hooks can be used to deliver alerts. Launch of the alert package can be scheduled in cron.
Prepare the packet directory:
cd db_converter
mkdir packets/alert_max_connectionsMake the packet and alert configuration:
cat > alert_max_connections/01_step.sql << EOL
SELECT format('Low number of free connections: %s (%s) [max_connections = %s]', T.c, T.v || '%', T.s) as alert
FROM (
SELECT
round(((select count(1) from pg_stat_activity) * 100)::numeric / setting::integer, 2) as v,
setting::integer - (select count(1) from pg_stat_activity) as c,
setting as s
FROM pg_settings
WHERE name = 'max_connections'
) T WHERE T.v > 70
EOL
cat > alert_max_connections/meta_data.json << EOL
{
"description": "Check available connections",
"type": "read_only",
"client_min_messages": "INFO",
"hook": {
"type": "matterhook",
"username": "db_converter",
"verbosity": ["resultset"],
"channel": "dbc-alert",
"message": ":warning: @here Found problem with max_connections:"
}
}If statement returns a result, then a hook will be triggered to send a message to the mattermost.
Run packet and check output:
python3 db_converter.py --packet-name=alert_max_connections --db-name=some_db
>>
----------------------------------------------------------------------
| alert |
----------------------------------------------------------------------
| Low number of free connections: 10 (10%) [max_connections = 100] |With a properly configured integration with mattermost, the message will be delivered to the corresponding chat.
Prepare the packet directory:
cd db_converter
mkdir packets/alert_locksMake the packet and alert configuration:
cat > alert_locks/01_step.sql << EOL
SELECT
blocking_locks.pid AS blocker_pid,
blocking_activity.usename AS blocker_user,
substring(blocking_activity.query FROM 0 FOR 64) AS blocker_statement,
blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
substring(blocked_activity.query FROM 0 FOR 64) AS blocked_statement,
now() - blocked_activity.state_change as blocked_wait_time
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.GRANTED and now() - blocked_activity.state_change > '1 minute'::interval
LIMIT 10
EOL
cat > alert_locks/meta_data.json << EOL
{
"description": "Check locks",
"type": "read_only",
"client_min_messages": "INFO",
"hook": {
"type": "matterhook",
"username": "db_converter",
"verbosity": ["resultset"],
"channel": "dbc-alert",
"message": ":warning: @here Found locks:"
}
}
EOLRun packet and check output:
python3 db_converter.py --packet-name=alert_locks --db-name=some_db
>>
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
| blocker_pid | blocker_user | blocker_statement | blocked_pid | blocked_user | blocked_statement | blocked_wait_time |
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 31651 | postgres | select * from dbc.dbc_actions; select pg_sleep(5000); | 31670 | postgres | vacuum full dbc.dbc_actions; | 0:41:33.131784 |