diff options
-rw-r--r-- | contrib/pg_buffercache/expected/pg_buffercache.out | 64 | ||||
-rw-r--r-- | contrib/pg_buffercache/pg_buffercache--1.5--1.6.sql | 24 | ||||
-rw-r--r-- | contrib/pg_buffercache/pg_buffercache_pages.c | 127 | ||||
-rw-r--r-- | contrib/pg_buffercache/sql/pg_buffercache.sql | 42 | ||||
-rw-r--r-- | doc/src/sgml/pgbuffercache.sgml | 75 | ||||
-rw-r--r-- | src/backend/storage/buffer/bufmgr.c | 175 | ||||
-rw-r--r-- | src/include/storage/bufmgr.h | 9 | ||||
-rw-r--r-- | src/test/modules/test_aio/test_aio.c | 6 |
8 files changed, 483 insertions, 39 deletions
diff --git a/contrib/pg_buffercache/expected/pg_buffercache.out b/contrib/pg_buffercache/expected/pg_buffercache.out index b745dc69eae..9a9216dc7b1 100644 --- a/contrib/pg_buffercache/expected/pg_buffercache.out +++ b/contrib/pg_buffercache/expected/pg_buffercache.out @@ -55,3 +55,67 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts(); t (1 row) +RESET role; +------ +---- Test pg_buffercache_evict* functions +------ +CREATE ROLE regress_buffercache_normal; +SET ROLE regress_buffercache_normal; +-- These should fail because they need to be called as SUPERUSER +SELECT * FROM pg_buffercache_evict(1); +ERROR: must be superuser to use pg_buffercache_evict() +SELECT * FROM pg_buffercache_evict_relation(1); +ERROR: must be superuser to use pg_buffercache_evict_relation() +SELECT * FROM pg_buffercache_evict_all(); +ERROR: must be superuser to use pg_buffercache_evict_all() +RESET ROLE; +-- These should return nothing, because these are STRICT functions +SELECT * FROM pg_buffercache_evict(NULL); + buffer_evicted | buffer_flushed +----------------+---------------- + | +(1 row) + +SELECT * FROM pg_buffercache_evict_relation(NULL); + buffers_evicted | buffers_flushed | buffers_skipped +-----------------+-----------------+----------------- + | | +(1 row) + +-- These should fail because they are not called by valid range of buffers +-- Number of the shared buffers are limited by max integer +SELECT 2147483647 max_buffers \gset +SELECT * FROM pg_buffercache_evict(-1); +ERROR: bad buffer ID: -1 +SELECT * FROM pg_buffercache_evict(0); +ERROR: bad buffer ID: 0 +SELECT * FROM pg_buffercache_evict(:max_buffers); +ERROR: bad buffer ID: 2147483647 +-- This should fail because pg_buffercache_evict_relation() doesn't accept +-- local relations +CREATE TEMP TABLE temp_pg_buffercache(); +SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache'); +ERROR: relation uses local buffers, pg_buffercache_evict_relation() is intended to be used for shared buffers only +DROP TABLE temp_pg_buffercache; +-- These shouldn't fail +SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1); + ?column? +---------- + t +(1 row) + +SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_all(); + ?column? +---------- + t +(1 row) + +CREATE TABLE shared_pg_buffercache(); +SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg_buffercache'); + ?column? +---------- + t +(1 row) + +DROP TABLE shared_pg_buffercache; +DROP ROLE regress_buffercache_normal; diff --git a/contrib/pg_buffercache/pg_buffercache--1.5--1.6.sql b/contrib/pg_buffercache/pg_buffercache--1.5--1.6.sql index f6668e41b37..85a23c65164 100644 --- a/contrib/pg_buffercache/pg_buffercache--1.5--1.6.sql +++ b/contrib/pg_buffercache/pg_buffercache--1.5--1.6.sql @@ -20,3 +20,27 @@ REVOKE ALL ON pg_buffercache_numa FROM PUBLIC; GRANT EXECUTE ON FUNCTION pg_buffercache_numa_pages() TO pg_monitor; GRANT SELECT ON pg_buffercache_numa TO pg_monitor; + + +DROP FUNCTION pg_buffercache_evict(integer); +CREATE FUNCTION pg_buffercache_evict( + IN int, + OUT buffer_evicted boolean, + OUT buffer_flushed boolean) +AS 'MODULE_PATHNAME', 'pg_buffercache_evict' +LANGUAGE C PARALLEL SAFE VOLATILE STRICT; + +CREATE FUNCTION pg_buffercache_evict_relation( + IN regclass, + OUT buffers_evicted int4, + OUT buffers_flushed int4, + OUT buffers_skipped int4) +AS 'MODULE_PATHNAME', 'pg_buffercache_evict_relation' +LANGUAGE C PARALLEL SAFE VOLATILE STRICT; + +CREATE FUNCTION pg_buffercache_evict_all( + OUT buffers_evicted int4, + OUT buffers_flushed int4, + OUT buffers_skipped int4) +AS 'MODULE_PATHNAME', 'pg_buffercache_evict_all' +LANGUAGE C PARALLEL SAFE VOLATILE; diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c index a702a47efe9..54c83601418 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -9,17 +9,22 @@ #include "postgres.h" #include "access/htup_details.h" +#include "access/relation.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "port/pg_numa.h" #include "storage/buf_internals.h" #include "storage/bufmgr.h" +#include "utils/rel.h" #define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8 #define NUM_BUFFERCACHE_PAGES_ELEM 9 #define NUM_BUFFERCACHE_SUMMARY_ELEM 5 #define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4 +#define NUM_BUFFERCACHE_EVICT_ELEM 2 +#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3 +#define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3 #define NUM_BUFFERCACHE_NUMA_ELEM 3 @@ -93,6 +98,8 @@ PG_FUNCTION_INFO_V1(pg_buffercache_numa_pages); PG_FUNCTION_INFO_V1(pg_buffercache_summary); PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts); PG_FUNCTION_INFO_V1(pg_buffercache_evict); +PG_FUNCTION_INFO_V1(pg_buffercache_evict_relation); +PG_FUNCTION_INFO_V1(pg_buffercache_evict_all); /* Only need to touch memory once per backend process lifetime */ @@ -638,20 +645,130 @@ pg_buffercache_usage_counts(PG_FUNCTION_ARGS) } /* + * Helper function to check if the user has superuser privileges. + */ +static void +pg_buffercache_superuser_check(char *func_name) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use %s()", + func_name))); +} + +/* * Try to evict a shared buffer. */ Datum pg_buffercache_evict(PG_FUNCTION_ARGS) { + Datum result; + TupleDesc tupledesc; + HeapTuple tuple; + Datum values[NUM_BUFFERCACHE_EVICT_ELEM]; + bool nulls[NUM_BUFFERCACHE_EVICT_ELEM] = {0}; + Buffer buf = PG_GETARG_INT32(0); + bool buffer_flushed; - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to use pg_buffercache_evict function"))); + if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + pg_buffercache_superuser_check("pg_buffercache_evict"); if (buf < 1 || buf > NBuffers) elog(ERROR, "bad buffer ID: %d", buf); - PG_RETURN_BOOL(EvictUnpinnedBuffer(buf)); + values[0] = BoolGetDatum(EvictUnpinnedBuffer(buf, &buffer_flushed)); + values[1] = BoolGetDatum(buffer_flushed); + + tuple = heap_form_tuple(tupledesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} + +/* + * Try to evict specified relation. + */ +Datum +pg_buffercache_evict_relation(PG_FUNCTION_ARGS) +{ + Datum result; + TupleDesc tupledesc; + HeapTuple tuple; + Datum values[NUM_BUFFERCACHE_EVICT_RELATION_ELEM]; + bool nulls[NUM_BUFFERCACHE_EVICT_RELATION_ELEM] = {0}; + + Oid relOid; + Relation rel; + + int32 buffers_evicted = 0; + int32 buffers_flushed = 0; + int32 buffers_skipped = 0; + + if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + pg_buffercache_superuser_check("pg_buffercache_evict_relation"); + + relOid = PG_GETARG_OID(0); + + rel = relation_open(relOid, AccessShareLock); + + if (RelationUsesLocalBuffers(rel)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only", + "pg_buffercache_evict_relation"))); + + EvictRelUnpinnedBuffers(rel, &buffers_evicted, &buffers_flushed, + &buffers_skipped); + + relation_close(rel, AccessShareLock); + + values[0] = Int32GetDatum(buffers_evicted); + values[1] = Int32GetDatum(buffers_flushed); + values[2] = Int32GetDatum(buffers_skipped); + + tuple = heap_form_tuple(tupledesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} + + +/* + * Try to evict all shared buffers. + */ +Datum +pg_buffercache_evict_all(PG_FUNCTION_ARGS) +{ + Datum result; + TupleDesc tupledesc; + HeapTuple tuple; + Datum values[NUM_BUFFERCACHE_EVICT_ALL_ELEM]; + bool nulls[NUM_BUFFERCACHE_EVICT_ALL_ELEM] = {0}; + + int32 buffers_evicted = 0; + int32 buffers_flushed = 0; + int32 buffers_skipped = 0; + + if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + pg_buffercache_superuser_check("pg_buffercache_evict_all"); + + EvictAllUnpinnedBuffers(&buffers_evicted, &buffers_flushed, + &buffers_skipped); + + values[0] = Int32GetDatum(buffers_evicted); + values[1] = Int32GetDatum(buffers_flushed); + values[2] = Int32GetDatum(buffers_skipped); + + tuple = heap_form_tuple(tupledesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); } diff --git a/contrib/pg_buffercache/sql/pg_buffercache.sql b/contrib/pg_buffercache/sql/pg_buffercache.sql index 944fbb1beae..47cca1907c7 100644 --- a/contrib/pg_buffercache/sql/pg_buffercache.sql +++ b/contrib/pg_buffercache/sql/pg_buffercache.sql @@ -26,3 +26,45 @@ SET ROLE pg_monitor; SELECT count(*) > 0 FROM pg_buffercache; SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary(); SELECT count(*) > 0 FROM pg_buffercache_usage_counts(); +RESET role; + + +------ +---- Test pg_buffercache_evict* functions +------ + +CREATE ROLE regress_buffercache_normal; +SET ROLE regress_buffercache_normal; + +-- These should fail because they need to be called as SUPERUSER +SELECT * FROM pg_buffercache_evict(1); +SELECT * FROM pg_buffercache_evict_relation(1); +SELECT * FROM pg_buffercache_evict_all(); + +RESET ROLE; + +-- These should return nothing, because these are STRICT functions +SELECT * FROM pg_buffercache_evict(NULL); +SELECT * FROM pg_buffercache_evict_relation(NULL); + +-- These should fail because they are not called by valid range of buffers +-- Number of the shared buffers are limited by max integer +SELECT 2147483647 max_buffers \gset +SELECT * FROM pg_buffercache_evict(-1); +SELECT * FROM pg_buffercache_evict(0); +SELECT * FROM pg_buffercache_evict(:max_buffers); + +-- This should fail because pg_buffercache_evict_relation() doesn't accept +-- local relations +CREATE TEMP TABLE temp_pg_buffercache(); +SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache'); +DROP TABLE temp_pg_buffercache; + +-- These shouldn't fail +SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1); +SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_all(); +CREATE TABLE shared_pg_buffercache(); +SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg_buffercache'); +DROP TABLE shared_pg_buffercache; + +DROP ROLE regress_buffercache_normal; diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml index b5050cd7343..ef5732942f7 100644 --- a/doc/src/sgml/pgbuffercache.sgml +++ b/doc/src/sgml/pgbuffercache.sgml @@ -27,14 +27,24 @@ <primary>pg_buffercache_evict</primary> </indexterm> + <indexterm> + <primary>pg_buffercache_evict_relation</primary> + </indexterm> + + <indexterm> + <primary>pg_buffercache_evict_all</primary> + </indexterm> + <para> This module provides the <function>pg_buffercache_pages()</function> function (wrapped in the <structname>pg_buffercache</structname> view), <function>pg_buffercache_numa_pages()</function> function (wrapped in the <structname>pg_buffercache_numa</structname> view), the <function>pg_buffercache_summary()</function> function, the - <function>pg_buffercache_usage_counts()</function> function and - the <function>pg_buffercache_evict()</function> function. + <function>pg_buffercache_usage_counts()</function> function, the + <function>pg_buffercache_evict()</function>, the + <function>pg_buffercache_evict_relation()</function> function and the + <function>pg_buffercache_evict_all()</function> function. </para> <para> @@ -76,6 +86,19 @@ function is restricted to superusers only. </para> + <para> + The <function>pg_buffercache_evict_relation()</function> function allows all + unpinned shared buffers in the relation to be evicted from the buffer pool + given a relation identifier. Use of this function is restricted to + superusers only. + </para> + + <para> + The <function>pg_buffercache_evict_all()</function> function allows all + unpinned shared buffers to be evicted in the buffer pool. Use of this + function is restricted to superusers only. + </para> + <sect2 id="pgbuffercache-pg-buffercache"> <title>The <structname>pg_buffercache</structname> View</title> @@ -452,11 +475,49 @@ <para> The <function>pg_buffercache_evict()</function> function takes a buffer identifier, as shown in the <structfield>bufferid</structfield> column of - the <structname>pg_buffercache</structname> view. It returns true on success, - and false if the buffer wasn't valid, if it couldn't be evicted because it - was pinned, or if it became dirty again after an attempt to write it out. - The result is immediately out of date upon return, as the buffer might - become valid again at any time due to concurrent activity. The function is + the <structname>pg_buffercache</structname> view. It returns information + about whether the buffer was evicted and flushed. The buffer_evicted + column is true on success, and false if the buffer wasn't valid, if it + couldn't be evicted because it was pinned, or if it became dirty again + after an attempt to write it out. The buffer_flushed column is true if the + buffer was flushed. This does not necessarily mean that buffer was flushed + by us, it might be flushed by someone else. The result is immediately out + of date upon return, as the buffer might become valid again at any time due + to concurrent activity. The function is intended for developer testing + only. + </para> + </sect2> + + <sect2 id="pgbuffercache-pg-buffercache-evict-relation"> + <title>The <structname>pg_buffercache_evict_relation</structname> Function</title> + <para> + The <function>pg_buffercache_evict_relation()</function> function is very + similar to the <function>pg_buffercache_evict()</function> function. The + difference is that the <function>pg_buffercache_evict_relation()</function> + takes a relation identifier instead of buffer identifier. It tries to + evict all buffers for all forks in that relation. + + It returns the number of evicted buffers, flushed buffers and the number of + buffers that could not be evicted. Flushed buffers haven't necessarily + been flushed by us, they might have been flushed by someone else. The + result is immediately out of date upon return, as buffers might immediately + be read back in due to concurrent activity. The function is intended for + developer testing only. + </para> + </sect2> + + <sect2 id="pgbuffercache-pg-buffercache-evict-all"> + <title>The <structname>pg_buffercache_evict_all</structname> Function</title> + <para> + The <function>pg_buffercache_evict_all()</function> function is very + similar to the <function>pg_buffercache_evict()</function> function. The + difference is, the <function>pg_buffercache_evict_all()</function> function + does not take an argument; instead it tries to evict all buffers in the + buffer pool. It returns the number of evicted buffers, flushed buffers and + the number of buffers that could not be evicted. Flushed buffers haven't + necessarily been flushed by us, they might have been flushed by someone + else. The result is immediately out of date upon return, as buffers might + immediately be read back in due to concurrent activity. The function is intended for developer testing only. </para> </sect2> diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 941d7fa3d94..db8f2b1754e 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -6510,37 +6510,20 @@ ResOwnerPrintBufferPin(Datum res) } /* - * Try to evict the current block in a shared buffer. - * - * This function is intended for testing/development use only! - * - * To succeed, the buffer must not be pinned on entry, so if the caller had a - * particular block in mind, it might already have been replaced by some other - * block by the time this function runs. It's also unpinned on return, so the - * buffer might be occupied again by the time control is returned, potentially - * even by the same block. This inherent raciness without other interlocking - * makes the function unsuitable for non-testing usage. - * - * Returns true if the buffer was valid and it has now been made invalid. - * Returns false if it wasn't valid, if it couldn't be evicted due to a pin, - * or if the buffer becomes dirty again while we're trying to write it out. + * Helper function to evict unpinned buffer whose buffer header lock is + * already acquired. */ -bool -EvictUnpinnedBuffer(Buffer buf) +static bool +EvictUnpinnedBufferInternal(BufferDesc *desc, bool *buffer_flushed) { - BufferDesc *desc; uint32 buf_state; bool result; - /* Make sure we can pin the buffer. */ - ResourceOwnerEnlarge(CurrentResourceOwner); - ReservePrivateRefCountEntry(); + *buffer_flushed = false; - Assert(!BufferIsLocal(buf)); - desc = GetBufferDescriptor(buf - 1); + buf_state = pg_atomic_read_u32(&(desc->state)); + Assert(buf_state & BM_LOCKED); - /* Lock the header and check if it's valid. */ - buf_state = LockBufHdr(desc); if ((buf_state & BM_VALID) == 0) { UnlockBufHdr(desc, buf_state); @@ -6561,6 +6544,7 @@ EvictUnpinnedBuffer(Buffer buf) { LWLockAcquire(BufferDescriptorGetContentLock(desc), LW_SHARED); FlushBuffer(desc, NULL, IOOBJECT_RELATION, IOCONTEXT_NORMAL); + *buffer_flushed = true; LWLockRelease(BufferDescriptorGetContentLock(desc)); } @@ -6573,6 +6557,149 @@ EvictUnpinnedBuffer(Buffer buf) } /* + * Try to evict the current block in a shared buffer. + * + * This function is intended for testing/development use only! + * + * To succeed, the buffer must not be pinned on entry, so if the caller had a + * particular block in mind, it might already have been replaced by some other + * block by the time this function runs. It's also unpinned on return, so the + * buffer might be occupied again by the time control is returned, potentially + * even by the same block. This inherent raciness without other interlocking + * makes the function unsuitable for non-testing usage. + * + * *buffer_flushed is set to true if the buffer was dirty and has been + * flushed, false otherwise. However, *buffer_flushed=true does not + * necessarily mean that we flushed the buffer, it could have been flushed by + * someone else. + * + * Returns true if the buffer was valid and it has now been made invalid. + * Returns false if it wasn't valid, if it couldn't be evicted due to a pin, + * or if the buffer becomes dirty again while we're trying to write it out. + */ +bool +EvictUnpinnedBuffer(Buffer buf, bool *buffer_flushed) +{ + BufferDesc *desc; + + Assert(BufferIsValid(buf) && !BufferIsLocal(buf)); + + /* Make sure we can pin the buffer. */ + ResourceOwnerEnlarge(CurrentResourceOwner); + ReservePrivateRefCountEntry(); + + desc = GetBufferDescriptor(buf - 1); + LockBufHdr(desc); + + return EvictUnpinnedBufferInternal(desc, buffer_flushed); +} + +/* + * Try to evict all the shared buffers. + * + * This function is intended for testing/development use only! See + * EvictUnpinnedBuffer(). + * + * The buffers_* parameters are mandatory and indicate the total count of + * buffers that: + * - buffers_evicted - were evicted + * - buffers_flushed - were flushed + * - buffers_skipped - could not be evicted + */ +void +EvictAllUnpinnedBuffers(int32 *buffers_evicted, int32 *buffers_flushed, + int32 *buffers_skipped) +{ + *buffers_evicted = 0; + *buffers_skipped = 0; + *buffers_flushed = 0; + + for (int buf = 1; buf <= NBuffers; buf++) + { + BufferDesc *desc = GetBufferDescriptor(buf - 1); + uint32 buf_state; + bool buffer_flushed; + + buf_state = pg_atomic_read_u32(&desc->state); + if (!(buf_state & BM_VALID)) + continue; + + ResourceOwnerEnlarge(CurrentResourceOwner); + ReservePrivateRefCountEntry(); + + LockBufHdr(desc); + + if (EvictUnpinnedBufferInternal(desc, &buffer_flushed)) + (*buffers_evicted)++; + else + (*buffers_skipped)++; + + if (buffer_flushed) + (*buffers_flushed)++; + } +} + +/* + * Try to evict all the shared buffers containing provided relation's pages. + * + * This function is intended for testing/development use only! See + * EvictUnpinnedBuffer(). + * + * The caller must hold at least AccessShareLock on the relation to prevent + * the relation from being dropped. + * + * The buffers_* parameters are mandatory and indicate the total count of + * buffers that: + * - buffers_evicted - were evicted + * - buffers_flushed - were flushed + * - buffers_skipped - could not be evicted + */ +void +EvictRelUnpinnedBuffers(Relation rel, int32 *buffers_evicted, + int32 *buffers_flushed, int32 *buffers_skipped) +{ + Assert(!RelationUsesLocalBuffers(rel)); + + *buffers_skipped = 0; + *buffers_evicted = 0; + *buffers_flushed = 0; + + for (int buf = 1; buf <= NBuffers; buf++) + { + BufferDesc *desc = GetBufferDescriptor(buf - 1); + uint32 buf_state = pg_atomic_read_u32(&(desc->state)); + bool buffer_flushed; + + /* An unlocked precheck should be safe and saves some cycles. */ + if ((buf_state & BM_VALID) == 0 || + !BufTagMatchesRelFileLocator(&desc->tag, &rel->rd_locator)) + continue; + + /* Make sure we can pin the buffer. */ + ResourceOwnerEnlarge(CurrentResourceOwner); + ReservePrivateRefCountEntry(); + + buf_state = LockBufHdr(desc); + + /* recheck, could have changed without the lock */ + if ((buf_state & BM_VALID) == 0 || + !BufTagMatchesRelFileLocator(&desc->tag, &rel->rd_locator)) + { + UnlockBufHdr(desc, buf_state); + continue; + } + + if (EvictUnpinnedBufferInternal(desc, &buffer_flushed)) + (*buffers_evicted)++; + else + (*buffers_skipped)++; + + if (buffer_flushed) + (*buffers_flushed)++; + } +} + +/* * Generic implementation of the AIO handle staging callback for readv/writev * on local/shared buffers. * diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index f2192ceb271..96150a6cfe9 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -304,7 +304,14 @@ extern uint32 GetAdditionalLocalPinLimit(void); extern void LimitAdditionalPins(uint32 *additional_pins); extern void LimitAdditionalLocalPins(uint32 *additional_pins); -extern bool EvictUnpinnedBuffer(Buffer buf); +extern bool EvictUnpinnedBuffer(Buffer buf, bool *buffer_flushed); +extern void EvictAllUnpinnedBuffers(int32 *buffers_evicted, + int32 *buffers_flushed, + int32 *buffers_skipped); +extern void EvictRelUnpinnedBuffers(Relation rel, + int32 *buffers_evicted, + int32 *buffers_flushed, + int32 *buffers_skipped); /* in buf_init.c */ extern void BufferManagerShmemInit(void); diff --git a/src/test/modules/test_aio/test_aio.c b/src/test/modules/test_aio/test_aio.c index bef0ecd9007..1d776010ef4 100644 --- a/src/test/modules/test_aio/test_aio.c +++ b/src/test/modules/test_aio/test_aio.c @@ -203,6 +203,7 @@ modify_rel_block(PG_FUNCTION_ARGS) bool corrupt_header = PG_GETARG_BOOL(3); bool corrupt_checksum = PG_GETARG_BOOL(4); Page page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0); + bool flushed; Relation rel; Buffer buf; PageHeader ph; @@ -237,7 +238,7 @@ modify_rel_block(PG_FUNCTION_ARGS) if (BufferIsLocal(buf)) InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true); else - EvictUnpinnedBuffer(buf); + EvictUnpinnedBuffer(buf, &flushed); /* * Now modify the page as asked for by the caller. @@ -478,6 +479,7 @@ invalidate_rel_block(PG_FUNCTION_ARGS) BufferDesc *buf_hdr = BufferIsLocal(buf) ? GetLocalBufferDescriptor(-buf - 1) : GetBufferDescriptor(buf - 1); + bool flushed; LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); @@ -493,7 +495,7 @@ invalidate_rel_block(PG_FUNCTION_ARGS) if (BufferIsLocal(buf)) InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true); - else if (!EvictUnpinnedBuffer(buf)) + else if (!EvictUnpinnedBuffer(buf, &flushed)) elog(ERROR, "couldn't evict"); } } |