blob: 81304b6c317e9e3e21dad2741ad208b1a268a406 [file] [log] [blame]
-- This test validates that dirty relfiles are sync'ed by checkpoint
-- process. It simulates the following scenario.
--
-- * Start with a clean slate - ensure that all dirty buffers are
-- flushed by creating a checkpoint.
-- * Suspend bgwriter at the beginning of main loop.
-- * Suspend checkpointer process by injecting skip fault.
-- * Make buffers of two tables dirty (fsync_test1 and fsync_test2).
-- * Resume bgwriter so that it performs a round of writing out dirty
-- buffers.
-- * Resume checkpointer and let it fsync the two dirty relfiles
-- corresponding to fsync_test1 and fsync_test2 tables.
-- * Verify that at least two files were fsync'ed by checkpointer.
--
-- the hit times of fsync_counter is undetermined, we accept anything
-- between 4-9.
-- start_matchsubs
-- m/num times hit:\'[4-9]\'/
-- s/num times hit:\'[4-9]\'/num times hit:\'greater_than_two\'/
-- end_matchsubs
-- Prevent autovacuum from dirty-ing buffers.
alter system set autovacuum = off;
select gp_segment_id, pg_reload_conf() from gp_id union select gp_segment_id, pg_reload_conf() from gp_dist_random('gp_id');
begin;
create or replace function wait_until_dirty_buffer_flushed() returns text as $$
begin
for i in 1..60 loop
if ((select count(*) = 0 from dirty_buffers_on_qes() as (tablespace oid, database oid, relfilenode oid, block int)) AND
(select count(*) = 0 from dirty_buffers_on_qd() as (tablespace oid, database oid, relfilenode oid, block int))) then
return 'OK'; /* in func */
end if; /* in func */
perform pg_sleep(0.1); /* in func */
checkpoint; /* in func */
end loop; /* in func */
return 'Fail'; /* in func */
end; /* in func */
$$ language plpgsql;
create function num_dirty_on_qes(relid oid) returns setof bigint as
$$
declare
rfnode oid;
result int4;
begin
select relfilenode into rfnode from pg_class where oid=$1;
select count(*) into result from dirty_buffers_on_qes()
as (tablespace oid, database oid, relfilenode oid, block int)
where relfilenode = rfnode;
return next result;
end
$$ language plpgsql execute on all segments;
-- Wait until number of dirty buffers for the specified relfiles drops
-- to 0 or timeout occurs. Returns false if timeout occurred.
create function wait_for_bgwriter(
relid oid,
timeout int)
returns boolean as
$$
declare
d bigint;
i int;
begin
i := 0;
loop
select sum(nd) into d from num_dirty_on_qes($1) nd;
if (d = 0) then
return true;
end if;
if i >= $2 then
return false;
end if;
perform pg_sleep(.5);
i := i + 1;
end loop;
end;
$$ language plpgsql;
create table fsync_test1(a int, b int) distributed by (a);
create table fsync_test2(a int, b int) distributed by (a);
insert into fsync_test1 select i, i from generate_series(1,100)i;
insert into fsync_test2 select -i, i from generate_series(1,100)i;
end;
vacuum pg_proc;
-- Reset all faults.
--
-- NOTICE: important.
--
-- we use gp_inject_fault_infinite here instead of
-- gp_inject_fault so cache of pg_proc that contains
-- gp_inject_fault_infinite is loaded before checkpoint and
-- the following gp_inject_fault_infinite don't dirty the
-- buffer again.
select gp_inject_fault_infinite('all', 'reset', dbid) from gp_segment_configuration;
-- Start with a clean slate (no dirty buffers).
checkpoint;
-- Ensure no buffers are dirty before we start.
select wait_until_dirty_buffer_flushed();
-- Skip checkpoints.
select gp_inject_fault_infinite('checkpoint', 'skip', dbid)
from gp_segment_configuration where role = 'p' and content > -1;
-- Suspend bgwriter.
select gp_inject_fault_infinite('fault_in_background_writer_main', 'suspend', dbid)
from gp_segment_configuration where role = 'p' and content > -1;
-- Make buffers dirty. At least two relfiles must be sync'ed during
-- next checkpoint.
insert into fsync_test1 select i, i from generate_series(1,1000)i;
delete from fsync_test2;
-- Should return at least one dirty buffer.
select sum(nd) > 0 as passed from num_dirty_on_qes('fsync_test1'::regclass) nd;
select sum(nd) > 0 as passed from num_dirty_on_qes('fsync_test2'::regclass) nd;
-- Flush all dirty pages by BgBufferSync()
select gp_inject_fault_infinite('bg_buffer_sync_default_logic', 'skip', dbid)
from gp_segment_configuration where role = 'p' and content > -1;
-- Resume bgwriter.
select gp_inject_fault('fault_in_background_writer_main', 'resume', dbid)
from gp_segment_configuration where role = 'p' and content > -1;
-- Wait until bgwriter sweeps through and writes out dirty buffers.
-- The 10 indicates timeout in terms of number of iterations to be
-- executed by the waiting function. Each iteration sleeps for .5
-- seconds.
select wait_for_bgwriter('fsync_test1'::regclass, 25) as passed;
select wait_for_bgwriter('fsync_test2'::regclass, 25) as passed;
-- Inject fault to count relfiles fsync'ed by checkpointer.
select gp_inject_fault_infinite('fsync_counter', 'skip', dbid)
from gp_segment_configuration where role = 'p' and content > -1;
-- Resume checkpoints.
select gp_inject_fault('checkpoint', 'reset', dbid)
from gp_segment_configuration where role = 'p' and content > -1;
checkpoint;
-- There should be no dirty buffers after checkpoint.
select * from dirty_buffers_on_qes()
as (tablespace oid, database oid, relfilenode oid, block int);
-- Validate that the number of files fsync'ed by checkpointer is at
-- least 2. The two files fsync'ed should be corresponding to
-- fsync_test1 and fsync_test2 tables. `num times hit` is corresponding
-- to the number of files synced by `fsync_counter` fault type.
select gp_inject_fault('fsync_counter', 'status', 2::smallint);
-- Reset all faults.
select gp_inject_fault('all', 'reset', dbid) from gp_segment_configuration;
-- Reset autovacuum;
alter system set autovacuum = on;
select gp_segment_id, pg_reload_conf() from gp_id union select gp_segment_id, pg_reload_conf() from gp_dist_random('gp_id');