1-- This file and its contents are licensed under the Apache License 2.0. 2-- Please see the included NOTICE for copyright information and 3-- LICENSE-APACHE for a copy of the license. 4\set TEST_DBNAME_2 :TEST_DBNAME _2 5\c :TEST_DBNAME :ROLE_SUPERUSER 6-- start bgw since they are stopped for tests by default 7SELECT _timescaledb_internal.start_background_workers(); 8 start_background_workers 9-------------------------- 10 t 11(1 row) 12 13CREATE DATABASE :TEST_DBNAME_2; 14\c :TEST_DBNAME_2 :ROLE_SUPERUSER 15\ir include/bgw_launcher_utils.sql 16-- This file and its contents are licensed under the Apache License 2.0. 17-- Please see the included NOTICE for copyright information and 18-- LICENSE-APACHE for a copy of the license. 19-- Note on testing: need a couple wrappers that pg_sleep in a loop to wait for changes 20-- to appear in pg_stat_activity. 21-- Further Note: PG 9.6 changed what appeared in pg_stat_activity, so the launcher doesn't actually show up. 22-- we can still test its interactions with its children, but can't test some of the things specific to the launcher. 23-- So we've added some bits about the version number as needed. 24CREATE VIEW worker_counts as SELECT count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Launcher') as launcher, 25count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Scheduler' AND datname = :'TEST_DBNAME') as single_scheduler, 26count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Scheduler' AND datname = :'TEST_DBNAME_2') as single_2_scheduler, 27count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Scheduler' AND datname = 'template1') as template1_scheduler 28FROM pg_stat_activity; 29CREATE FUNCTION wait_worker_counts(launcher_ct INTEGER, scheduler1_ct INTEGER, scheduler2_ct INTEGER, template1_ct INTEGER) RETURNS BOOLEAN LANGUAGE PLPGSQL AS 30$BODY$ 31DECLARE 32r INTEGER; 33BEGIN 34FOR i in 1..10 35LOOP 36SELECT COUNT(*) from worker_counts where launcher = launcher_ct 37 AND single_scheduler = scheduler1_ct AND single_2_scheduler = scheduler2_ct and template1_scheduler = template1_ct into r; 38if(r < 1) THEN 39 PERFORM pg_sleep(0.1); 40 PERFORM pg_stat_clear_snapshot(); 41ELSE 42 --We have the correct counts! 43 RETURN TRUE; 44END IF; 45END LOOP; 46RETURN FALSE; 47END 48$BODY$; 49CREATE FUNCTION wait_for_bgw_scheduler(_datname NAME, _count INT DEFAULT 1, _ticks INT DEFAULT 10) RETURNS TEXT LANGUAGE PLPGSQL AS 50$BODY$ 51DECLARE 52 r INTEGER; 53BEGIN 54 FOR i in 1.._ticks 55 LOOP 56 SELECT count(*) 57 FROM pg_stat_activity 58 WHERE 59 application_name = 'TimescaleDB Background Worker Scheduler' AND 60 datname = _datname 61 INTO r; 62 63 IF(r <> _count) THEN 64 PERFORM pg_sleep(0.1); 65 PERFORM pg_stat_clear_snapshot(); 66 ELSE 67 RETURN 'BGW Scheduler found.'; 68 END IF; 69 70 END LOOP; 71 72 RETURN 'BGW Scheduler NOT found.'; 73 74END 75$BODY$; 76-- When we've connected to test db 2, we should be able to see the cluster launcher 77-- and the scheduler for test db in pg_stat_activity 78-- but test db 2 shouldn't have a scheduler because ext not created yet 79SELECT wait_worker_counts(1,1,0,0); 80 wait_worker_counts 81-------------------- 82 t 83(1 row) 84 85-- Now create the extension in test db 2 86SET client_min_messages = ERROR; 87CREATE EXTENSION timescaledb CASCADE; 88RESET client_min_messages; 89SELECT wait_worker_counts(1,1,1,0); 90 wait_worker_counts 91-------------------- 92 t 93(1 row) 94 95DROP DATABASE :TEST_DBNAME; 96-- Now the db_scheduler for test db should have disappeared 97SELECT wait_worker_counts(1,0,1,0); 98 wait_worker_counts 99-------------------- 100 t 101(1 row) 102 103-- Now let's restart the scheduler in test db 2 and make sure our backend_start changed 104SELECT backend_start as orig_backend_start 105FROM pg_stat_activity 106WHERE application_name = 'TimescaleDB Background Worker Scheduler' 107AND datname = :'TEST_DBNAME_2' \gset 108-- We'll do this in a txn so that we can see that the worker locks on our txn before continuing 109BEGIN; 110SELECT _timescaledb_internal.restart_background_workers(); 111 restart_background_workers 112---------------------------- 113 t 114(1 row) 115 116SELECT wait_worker_counts(1,0,1,0); 117 wait_worker_counts 118-------------------- 119 t 120(1 row) 121 122SELECT (backend_start > :'orig_backend_start'::timestamptz) backend_start_changed, 123(wait_event = 'virtualxid') wait_event_changed 124FROM pg_stat_activity 125WHERE application_name = 'TimescaleDB Background Worker Scheduler' 126AND datname = :'TEST_DBNAME_2'; 127 backend_start_changed | wait_event_changed 128-----------------------+-------------------- 129 t | t 130(1 row) 131 132COMMIT; 133SELECT wait_worker_counts(1,0,1,0); 134 wait_worker_counts 135-------------------- 136 t 137(1 row) 138 139SELECT (wait_event IS DISTINCT FROM 'virtualxid') wait_event_changed 140FROM pg_stat_activity 141WHERE application_name = 'TimescaleDB Background Worker Scheduler' 142AND datname = :'TEST_DBNAME_2'; 143 wait_event_changed 144-------------------- 145 t 146(1 row) 147 148-- Test stop 149SELECT _timescaledb_internal.stop_background_workers(); 150 stop_background_workers 151------------------------- 152 t 153(1 row) 154 155SELECT wait_worker_counts(1,0,0,0); 156 wait_worker_counts 157-------------------- 158 t 159(1 row) 160 161-- Make sure it doesn't break if we stop twice in a row 162SELECT _timescaledb_internal.stop_background_workers(); 163 stop_background_workers 164------------------------- 165 t 166(1 row) 167 168SELECT wait_worker_counts(1,0,0,0); 169 wait_worker_counts 170-------------------- 171 t 172(1 row) 173 174-- test start 175SELECT _timescaledb_internal.start_background_workers(); 176 start_background_workers 177-------------------------- 178 t 179(1 row) 180 181SELECT wait_worker_counts(1,0,1,0); 182 wait_worker_counts 183-------------------- 184 t 185(1 row) 186 187-- make sure start is idempotent 188SELECT backend_start as orig_backend_start 189FROM pg_stat_activity 190WHERE application_name = 'TimescaleDB Background Worker Scheduler' 191AND datname = :'TEST_DBNAME_2' \gset 192-- Since we're doing idempotency tests, we're also going to exercise our queue and start 20 times 193SELECT _timescaledb_internal.start_background_workers() as start_background_workers, * FROM generate_series(1,20); 194 start_background_workers | generate_series 195--------------------------+----------------- 196 t | 1 197 t | 2 198 t | 3 199 t | 4 200 t | 5 201 t | 6 202 t | 7 203 t | 8 204 t | 9 205 t | 10 206 t | 11 207 t | 12 208 t | 13 209 t | 14 210 t | 15 211 t | 16 212 t | 17 213 t | 18 214 t | 19 215 t | 20 216(20 rows) 217 218-- Here we're waiting to see if something shows up in pg_stat_activity, 219-- so we have to condition our loop in the opposite way. We'll only wait 220-- half a second in total as well so that tests don't take too long. 221CREATE FUNCTION wait_equals(TIMESTAMPTZ, TEXT) RETURNS BOOLEAN LANGUAGE PLPGSQL AS 222$BODY$ 223DECLARE 224r BOOLEAN; 225BEGIN 226FOR i in 1..5 227LOOP 228SELECT (backend_start = $1::timestamptz) backend_start_unchanged 229FROM pg_stat_activity 230WHERE application_name = 'TimescaleDB Background Worker Scheduler' 231AND datname = $2 into r; 232if(r) THEN 233 PERFORM pg_sleep(0.1); 234 PERFORM pg_stat_clear_snapshot(); 235ELSE 236 RETURN FALSE; 237END IF; 238END LOOP; 239RETURN TRUE; 240END 241$BODY$; 242select wait_equals(:'orig_backend_start', :'TEST_DBNAME_2'); 243 wait_equals 244------------- 245 t 246(1 row) 247 248-- Make sure restart starts a worker even if it is stopped 249SELECT _timescaledb_internal.stop_background_workers(); 250 stop_background_workers 251------------------------- 252 t 253(1 row) 254 255SELECT wait_worker_counts(1,0,0,0); 256 wait_worker_counts 257-------------------- 258 t 259(1 row) 260 261SELECT _timescaledb_internal.restart_background_workers(); 262 restart_background_workers 263---------------------------- 264 t 265(1 row) 266 267SELECT wait_worker_counts(1,0,1,0); 268 wait_worker_counts 269-------------------- 270 t 271(1 row) 272 273-- Make sure drop extension statement restarts the worker and on rollback it keeps running 274-- Now let's restart the scheduler and make sure our backend_start changed 275SELECT backend_start as orig_backend_start 276FROM pg_stat_activity 277WHERE application_name = 'TimescaleDB Background Worker Scheduler' 278AND datname = :'TEST_DBNAME_2' \gset 279BEGIN; 280DROP EXTENSION timescaledb; 281SELECT wait_worker_counts(1,0,1,0); 282 wait_worker_counts 283-------------------- 284 t 285(1 row) 286 287ROLLBACK; 288CREATE FUNCTION wait_greater(TIMESTAMPTZ,TEXT) RETURNS BOOLEAN LANGUAGE PLPGSQL AS 289$BODY$ 290DECLARE 291r BOOLEAN; 292BEGIN 293FOR i in 1..10 294LOOP 295SELECT (backend_start > $1::timestamptz) backend_start_changed 296FROM pg_stat_activity 297WHERE application_name = 'TimescaleDB Background Worker Scheduler' 298AND datname = $2 into r; 299if(NOT r) THEN 300 PERFORM pg_sleep(0.1); 301 PERFORM pg_stat_clear_snapshot(); 302ELSE 303 RETURN TRUE; 304END IF; 305END LOOP; 306RETURN FALSE; 307END 308$BODY$; 309SELECT wait_greater(:'orig_backend_start',:'TEST_DBNAME_2'); 310 wait_greater 311-------------- 312 t 313(1 row) 314 315-- Make sure canceling the launcher backend causes a restart of schedulers 316SELECT backend_start as orig_backend_start 317FROM pg_stat_activity 318WHERE application_name = 'TimescaleDB Background Worker Scheduler' 319AND datname = :'TEST_DBNAME_2' \gset 320SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE application_name = 'TimescaleDB Background Worker Launcher'; 321 pg_cancel_backend 322------------------- 323 t 324(1 row) 325 326SELECT wait_worker_counts(1,0,1,0); 327 wait_worker_counts 328-------------------- 329 t 330(1 row) 331 332SELECT wait_greater(:'orig_backend_start', :'TEST_DBNAME_2'); 333 wait_greater 334-------------- 335 t 336(1 row) 337 338-- Make sure running pre_restore function stops background workers 339SELECT timescaledb_pre_restore(); 340 timescaledb_pre_restore 341------------------------- 342 t 343(1 row) 344 345SELECT wait_worker_counts(1,0,0,0); 346 wait_worker_counts 347-------------------- 348 t 349(1 row) 350 351-- Make sure a restart with restoring on first starts the background worker 352BEGIN; 353SELECT _timescaledb_internal.restart_background_workers(); 354 restart_background_workers 355---------------------------- 356 t 357(1 row) 358 359SELECT wait_worker_counts(1,0,1,0); 360 wait_worker_counts 361-------------------- 362 t 363(1 row) 364 365COMMIT; 366-- Then the worker dies when it sees that restoring is on after the txn commits 367SELECT wait_worker_counts(1,0,0,0); 368 wait_worker_counts 369-------------------- 370 t 371(1 row) 372 373--And post_restore starts them 374BEGIN; 375SELECT timescaledb_post_restore(); 376 timescaledb_post_restore 377-------------------------- 378 t 379(1 row) 380 381SELECT wait_worker_counts(1,0,1,0); 382 wait_worker_counts 383-------------------- 384 t 385(1 row) 386 387COMMIT; 388-- And they stay started 389SELECT wait_worker_counts(1,0,1,0); 390 wait_worker_counts 391-------------------- 392 t 393(1 row) 394 395-- Make sure dropping the extension means that the scheduler is stopped 396BEGIN; 397DROP EXTENSION timescaledb; 398COMMIT; 399SELECT wait_worker_counts(1,0,0,0); 400 wait_worker_counts 401-------------------- 402 t 403(1 row) 404 405-- Test that background workers are stopped with DROP OWNED 406ALTER ROLE :ROLE_DEFAULT_PERM_USER WITH SUPERUSER; 407\c :TEST_DBNAME_2 :ROLE_DEFAULT_PERM_USER 408SET client_min_messages = ERROR; 409CREATE EXTENSION timescaledb CASCADE; 410RESET client_min_messages; 411-- Make sure there is 1 launcher and 1 bgw in test db 2 412SELECT wait_worker_counts(launcher_ct=>1, scheduler1_ct=> 0, scheduler2_ct=>1, template1_ct=>0); 413 wait_worker_counts 414-------------------- 415 t 416(1 row) 417 418-- drop a non-owner of the extension results in no change to worker counts 419DROP OWNED BY :ROLE_DEFAULT_PERM_USER_2; 420SELECT wait_worker_counts(launcher_ct=>1, scheduler1_ct=> 0, scheduler2_ct=>1, template1_ct=>0); 421 wait_worker_counts 422-------------------- 423 t 424(1 row) 425 426-- drop of owner of extension results in extension drop and a stop to the bgw 427DROP OWNED BY :ROLE_DEFAULT_PERM_USER; 428-- The worker in test db 2 is dead. Note that 0s are respected 429SELECT wait_worker_counts(launcher_ct=>1, scheduler1_ct=>0, scheduler2_ct=>0, template1_ct=>0); 430 wait_worker_counts 431-------------------- 432 t 433(1 row) 434 435\c :TEST_DBNAME_2 :ROLE_SUPERUSER 436ALTER ROLE :ROLE_DEFAULT_PERM_USER WITH NOSUPERUSER; 437-- Connect to the template1 database 438\c template1 439\ir include/bgw_launcher_utils.sql 440-- This file and its contents are licensed under the Apache License 2.0. 441-- Please see the included NOTICE for copyright information and 442-- LICENSE-APACHE for a copy of the license. 443-- Note on testing: need a couple wrappers that pg_sleep in a loop to wait for changes 444-- to appear in pg_stat_activity. 445-- Further Note: PG 9.6 changed what appeared in pg_stat_activity, so the launcher doesn't actually show up. 446-- we can still test its interactions with its children, but can't test some of the things specific to the launcher. 447-- So we've added some bits about the version number as needed. 448CREATE VIEW worker_counts as SELECT count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Launcher') as launcher, 449count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Scheduler' AND datname = :'TEST_DBNAME') as single_scheduler, 450count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Scheduler' AND datname = :'TEST_DBNAME_2') as single_2_scheduler, 451count(*) filter (WHERE application_name = 'TimescaleDB Background Worker Scheduler' AND datname = 'template1') as template1_scheduler 452FROM pg_stat_activity; 453CREATE FUNCTION wait_worker_counts(launcher_ct INTEGER, scheduler1_ct INTEGER, scheduler2_ct INTEGER, template1_ct INTEGER) RETURNS BOOLEAN LANGUAGE PLPGSQL AS 454$BODY$ 455DECLARE 456r INTEGER; 457BEGIN 458FOR i in 1..10 459LOOP 460SELECT COUNT(*) from worker_counts where launcher = launcher_ct 461 AND single_scheduler = scheduler1_ct AND single_2_scheduler = scheduler2_ct and template1_scheduler = template1_ct into r; 462if(r < 1) THEN 463 PERFORM pg_sleep(0.1); 464 PERFORM pg_stat_clear_snapshot(); 465ELSE 466 --We have the correct counts! 467 RETURN TRUE; 468END IF; 469END LOOP; 470RETURN FALSE; 471END 472$BODY$; 473CREATE FUNCTION wait_for_bgw_scheduler(_datname NAME, _count INT DEFAULT 1, _ticks INT DEFAULT 10) RETURNS TEXT LANGUAGE PLPGSQL AS 474$BODY$ 475DECLARE 476 r INTEGER; 477BEGIN 478 FOR i in 1.._ticks 479 LOOP 480 SELECT count(*) 481 FROM pg_stat_activity 482 WHERE 483 application_name = 'TimescaleDB Background Worker Scheduler' AND 484 datname = _datname 485 INTO r; 486 487 IF(r <> _count) THEN 488 PERFORM pg_sleep(0.1); 489 PERFORM pg_stat_clear_snapshot(); 490 ELSE 491 RETURN 'BGW Scheduler found.'; 492 END IF; 493 494 END LOOP; 495 496 RETURN 'BGW Scheduler NOT found.'; 497 498END 499$BODY$; 500BEGIN; 501-- Then create extension there in a txn and make sure we see a scheduler start 502SET client_min_messages = ERROR; 503CREATE EXTENSION timescaledb CASCADE; 504RESET client_min_messages; 505SELECT wait_worker_counts(1,0,0,1); 506 wait_worker_counts 507-------------------- 508 t 509(1 row) 510 511COMMIT; 512-- End our transaction and it should immediately exit because it's a template database. 513SELECT wait_worker_counts(1,0,0,0); 514 wait_worker_counts 515-------------------- 516 t 517(1 row) 518 519-- Clean up the template database, removing our test utilities etc 520\ir include/bgw_launcher_utils_cleanup.sql 521-- This file and its contents are licensed under the Apache License 2.0. 522-- Please see the included NOTICE for copyright information and 523-- LICENSE-APACHE for a copy of the license. 524DROP FUNCTION wait_worker_counts(integer, integer, integer, integer); 525DROP VIEW worker_counts; 526DROP FUNCTION wait_for_bgw_scheduler(name,int,int); 527\c :TEST_DBNAME_2 528-- Now try creating a DB from a template with the extension already installed. 529-- Make sure we see a scheduler start. 530CREATE DATABASE :TEST_DBNAME; 531SELECT wait_worker_counts(1,1,0,0); 532 wait_worker_counts 533-------------------- 534 t 535(1 row) 536 537DROP DATABASE :TEST_DBNAME; 538-- Now make sure that there's no race between create database and create extension. 539-- Although to be honest, this race probably wouldn't manifest in this test. 540\c template1 541DROP EXTENSION timescaledb; 542\c :TEST_DBNAME_2 543CREATE DATABASE :TEST_DBNAME; 544\c :TEST_DBNAME 545SET client_min_messages = ERROR; 546CREATE EXTENSION timescaledb; 547RESET client_min_messages; 548\c :TEST_DBNAME_2 549SELECT wait_worker_counts(1,1,0,0); 550 wait_worker_counts 551-------------------- 552 t 553(1 row) 554 555-- test rename database 556CREATE DATABASE db_rename_test; 557\c db_rename_test :ROLE_SUPERUSER 558SET client_min_messages=error; 559CREATE EXTENSION timescaledb; 560\c :TEST_DBNAME_2 :ROLE_SUPERUSER 561SELECT wait_for_bgw_scheduler('db_rename_test'); 562 wait_for_bgw_scheduler 563------------------------ 564 BGW Scheduler found. 565(1 row) 566 567ALTER DATABASE db_rename_test RENAME TO db_rename_test2; 568WARNING: you need to manually restart any running background workers after this command 569DROP DATABASE db_rename_test2; 570-- test create database with timescaledb database as template 571SELECT wait_for_bgw_scheduler(:'TEST_DBNAME'); 572 wait_for_bgw_scheduler 573------------------------ 574 BGW Scheduler found. 575(1 row) 576 577CREATE DATABASE db_from_template WITH TEMPLATE :TEST_DBNAME; 578SELECT wait_for_bgw_scheduler(:'TEST_DBNAME'); 579 wait_for_bgw_scheduler 580------------------------ 581 BGW Scheduler found. 582(1 row) 583 584DROP DATABASE db_from_template; 585-- test alter database set tablespace 586SET client_min_messages TO error; 587DROP TABLESPACE IF EXISTS tablespace1; 588RESET client_min_messages; 589CREATE TABLESPACE tablespace1 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE1_PATH; 590SELECT wait_for_bgw_scheduler(:'TEST_DBNAME'); 591 wait_for_bgw_scheduler 592------------------------ 593 BGW Scheduler found. 594(1 row) 595 596ALTER DATABASE :TEST_DBNAME SET TABLESPACE tablespace1; 597WARNING: you may need to manually restart any running background workers after this command 598-- clean up additional database 599\c :TEST_DBNAME :ROLE_SUPERUSER 600DROP DATABASE :TEST_DBNAME_2; 601