mirror of
https://github.com/postgres/postgres.git
synced 2025-12-07 00:02:34 -05:00
There are two ways for shared libraries to allocate their own LWLock tranches. One way is to call RequestNamedLWLockTranche() in a shmem_request_hook, which requires the library to be loaded via shared_preload_libraries. The other way is to call LWLockNewTrancheId(), which is not subject to the same restrictions. However, LWLockNewTrancheId() does require each backend to store the tranche's name in backend-local memory via LWLockRegisterTranche(). This API is a little cumbersome and leads to things like unhelpful pg_stat_activity.wait_event values in backends that haven't loaded the library. This commit moves these LWLock tranche names to shared memory, thus eliminating the need for each backend to call LWLockRegisterTranche(). Instead, the tranche name must be provided to LWLockNewTrancheId(), which immediately makes the name available to all backends. Since the tranche name array is append-only, lookups can ordinarily avoid locking as long as their local copy of the LWLock counter is greater than the requested tranche ID. One downside of this approach is that we now have a hard limit on both the length of tranche names (NAMEDATALEN-1 bytes) and the number of dynamically-allocated tranches (256). Besides a limit of NAMEDATALEN-1 bytes for tranche names registered via RequestNamedLWLockTranche(), no such limits previously existed. We could avoid these new limits by using dynamic shared memory, but the complexity involved didn't seem worth it. We briefly considered making the tranche limit user-configurable but ultimately decided against that, too. Since there is still a lot of time left in the v19 development cycle, it's possible we will revisit this choice. Author: Sami Imseih <samimseih@gmail.com> Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Rahila Syed <rahilasyed90@gmail.com> Reviewed-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/CAA5RZ0vvED3naph8My8Szv6DL4AxOVK3eTPS0qXsaKi%3DbVdW2A%40mail.gmail.com
261 lines
6.4 KiB
C
261 lines
6.4 KiB
C
/*--------------------------------------------------------------------------
|
|
*
|
|
* test_slru.c
|
|
* Test correctness of SLRU functions.
|
|
*
|
|
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/test/modules/test_slru/test_slru.c
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/slru.h"
|
|
#include "access/transam.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/ipc.h"
|
|
#include "storage/shmem.h"
|
|
#include "utils/builtins.h"
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
/*
|
|
* SQL-callable entry points
|
|
*/
|
|
PG_FUNCTION_INFO_V1(test_slru_page_write);
|
|
PG_FUNCTION_INFO_V1(test_slru_page_writeall);
|
|
PG_FUNCTION_INFO_V1(test_slru_page_read);
|
|
PG_FUNCTION_INFO_V1(test_slru_page_readonly);
|
|
PG_FUNCTION_INFO_V1(test_slru_page_exists);
|
|
PG_FUNCTION_INFO_V1(test_slru_page_sync);
|
|
PG_FUNCTION_INFO_V1(test_slru_page_delete);
|
|
PG_FUNCTION_INFO_V1(test_slru_page_truncate);
|
|
PG_FUNCTION_INFO_V1(test_slru_delete_all);
|
|
|
|
/* Number of SLRU page slots */
|
|
#define NUM_TEST_BUFFERS 16
|
|
|
|
static SlruCtlData TestSlruCtlData;
|
|
#define TestSlruCtl (&TestSlruCtlData)
|
|
|
|
static shmem_request_hook_type prev_shmem_request_hook = NULL;
|
|
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
|
|
|
|
static bool
|
|
test_slru_scan_cb(SlruCtl ctl, char *filename, int64 segpage, void *data)
|
|
{
|
|
elog(NOTICE, "Calling test_slru_scan_cb()");
|
|
return SlruScanDirCbDeleteAll(ctl, filename, segpage, data);
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_write(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 pageno = PG_GETARG_INT64(0);
|
|
char *data = text_to_cstring(PG_GETARG_TEXT_PP(1));
|
|
int slotno;
|
|
LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
|
|
|
|
LWLockAcquire(lock, LW_EXCLUSIVE);
|
|
slotno = SimpleLruZeroPage(TestSlruCtl, pageno);
|
|
|
|
/* these should match */
|
|
Assert(TestSlruCtl->shared->page_number[slotno] == pageno);
|
|
|
|
/* mark the page as dirty so as it would get written */
|
|
TestSlruCtl->shared->page_dirty[slotno] = true;
|
|
TestSlruCtl->shared->page_status[slotno] = SLRU_PAGE_VALID;
|
|
|
|
/* write given data to the page, up to the limit of the page */
|
|
strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
|
|
BLCKSZ - 1);
|
|
|
|
SimpleLruWritePage(TestSlruCtl, slotno);
|
|
LWLockRelease(lock);
|
|
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_writeall(PG_FUNCTION_ARGS)
|
|
{
|
|
SimpleLruWriteAll(TestSlruCtl, true);
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_read(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 pageno = PG_GETARG_INT64(0);
|
|
bool write_ok = PG_GETARG_BOOL(1);
|
|
char *data = NULL;
|
|
int slotno;
|
|
LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
|
|
|
|
/* find page in buffers, reading it if necessary */
|
|
LWLockAcquire(lock, LW_EXCLUSIVE);
|
|
slotno = SimpleLruReadPage(TestSlruCtl, pageno,
|
|
write_ok, InvalidTransactionId);
|
|
data = (char *) TestSlruCtl->shared->page_buffer[slotno];
|
|
LWLockRelease(lock);
|
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(data));
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_readonly(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 pageno = PG_GETARG_INT64(0);
|
|
char *data = NULL;
|
|
int slotno;
|
|
LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
|
|
|
|
/* find page in buffers, reading it if necessary */
|
|
slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
|
|
pageno,
|
|
InvalidTransactionId);
|
|
Assert(LWLockHeldByMe(lock));
|
|
data = (char *) TestSlruCtl->shared->page_buffer[slotno];
|
|
LWLockRelease(lock);
|
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(data));
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_exists(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 pageno = PG_GETARG_INT64(0);
|
|
bool found;
|
|
LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
|
|
|
|
LWLockAcquire(lock, LW_EXCLUSIVE);
|
|
found = SimpleLruDoesPhysicalPageExist(TestSlruCtl, pageno);
|
|
LWLockRelease(lock);
|
|
|
|
PG_RETURN_BOOL(found);
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_sync(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 pageno = PG_GETARG_INT64(0);
|
|
FileTag ftag;
|
|
char path[MAXPGPATH];
|
|
|
|
/* note that this flushes the full file a segment is located in */
|
|
ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
|
|
SlruSyncFileTag(TestSlruCtl, &ftag, path);
|
|
|
|
elog(NOTICE, "Called SlruSyncFileTag() for segment %" PRIu64 " on path %s",
|
|
ftag.segno, path);
|
|
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_delete(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 pageno = PG_GETARG_INT64(0);
|
|
FileTag ftag;
|
|
|
|
ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
|
|
SlruDeleteSegment(TestSlruCtl, ftag.segno);
|
|
|
|
elog(NOTICE, "Called SlruDeleteSegment() for segment %" PRIu64,
|
|
ftag.segno);
|
|
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
test_slru_page_truncate(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 pageno = PG_GETARG_INT64(0);
|
|
|
|
SimpleLruTruncate(TestSlruCtl, pageno);
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
Datum
|
|
test_slru_delete_all(PG_FUNCTION_ARGS)
|
|
{
|
|
/* this calls SlruScanDirCbDeleteAll() internally, ensuring deletion */
|
|
SlruScanDirectory(TestSlruCtl, test_slru_scan_cb, NULL);
|
|
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
/*
|
|
* Module load callbacks and initialization.
|
|
*/
|
|
|
|
static void
|
|
test_slru_shmem_request(void)
|
|
{
|
|
if (prev_shmem_request_hook)
|
|
prev_shmem_request_hook();
|
|
|
|
/* reserve shared memory for the test SLRU */
|
|
RequestAddinShmemSpace(SimpleLruShmemSize(NUM_TEST_BUFFERS, 0));
|
|
}
|
|
|
|
static bool
|
|
test_slru_page_precedes_logically(int64 page1, int64 page2)
|
|
{
|
|
return page1 < page2;
|
|
}
|
|
|
|
static void
|
|
test_slru_shmem_startup(void)
|
|
{
|
|
/*
|
|
* Short segments names are well tested elsewhere so in this test we are
|
|
* focusing on long names.
|
|
*/
|
|
const bool long_segment_names = true;
|
|
const char slru_dir_name[] = "pg_test_slru";
|
|
int test_tranche_id;
|
|
int test_buffer_tranche_id;
|
|
|
|
if (prev_shmem_startup_hook)
|
|
prev_shmem_startup_hook();
|
|
|
|
/*
|
|
* Create the SLRU directory if it does not exist yet, from the root of
|
|
* the data directory.
|
|
*/
|
|
(void) MakePGDirectory(slru_dir_name);
|
|
|
|
/* initialize the SLRU facility */
|
|
test_tranche_id = LWLockNewTrancheId("test_slru_tranche");
|
|
|
|
test_buffer_tranche_id = LWLockNewTrancheId("test_buffer_tranche");
|
|
|
|
TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
|
|
SimpleLruInit(TestSlruCtl, "TestSLRU",
|
|
NUM_TEST_BUFFERS, 0, slru_dir_name,
|
|
test_buffer_tranche_id, test_tranche_id, SYNC_HANDLER_NONE,
|
|
long_segment_names);
|
|
}
|
|
|
|
void
|
|
_PG_init(void)
|
|
{
|
|
if (!process_shared_preload_libraries_in_progress)
|
|
ereport(ERROR,
|
|
(errmsg("cannot load \"%s\" after startup", "test_slru"),
|
|
errdetail("\"%s\" must be loaded with \"shared_preload_libraries\".",
|
|
"test_slru")));
|
|
|
|
prev_shmem_request_hook = shmem_request_hook;
|
|
shmem_request_hook = test_slru_shmem_request;
|
|
|
|
prev_shmem_startup_hook = shmem_startup_hook;
|
|
shmem_startup_hook = test_slru_shmem_startup;
|
|
}
|