Line data Source code
1 : /*--------------------------------------------------------------------------
2 : *
3 : * test_slru.c
4 : * Test correctness of SLRU functions.
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/test/modules/test_slru/test_slru.c
11 : *
12 : * -------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include "access/slru.h"
18 : #include "access/transam.h"
19 : #include "miscadmin.h"
20 : #include "storage/fd.h"
21 : #include "storage/ipc.h"
22 : #include "storage/shmem.h"
23 : #include "utils/builtins.h"
24 :
25 4 : PG_MODULE_MAGIC;
26 :
27 : /*
28 : * SQL-callable entry points
29 : */
30 6 : PG_FUNCTION_INFO_V1(test_slru_page_write);
31 6 : PG_FUNCTION_INFO_V1(test_slru_page_writeall);
32 6 : PG_FUNCTION_INFO_V1(test_slru_page_read);
33 6 : PG_FUNCTION_INFO_V1(test_slru_page_readonly);
34 6 : PG_FUNCTION_INFO_V1(test_slru_page_exists);
35 6 : PG_FUNCTION_INFO_V1(test_slru_page_sync);
36 6 : PG_FUNCTION_INFO_V1(test_slru_page_delete);
37 6 : PG_FUNCTION_INFO_V1(test_slru_page_truncate);
38 6 : PG_FUNCTION_INFO_V1(test_slru_delete_all);
39 :
40 : /* Number of SLRU page slots */
41 : #define NUM_TEST_BUFFERS 16
42 :
43 : static SlruCtlData TestSlruCtlData;
44 : #define TestSlruCtl (&TestSlruCtlData)
45 :
46 : static shmem_request_hook_type prev_shmem_request_hook = NULL;
47 : static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
48 :
49 : static bool
50 2 : test_slru_scan_cb(SlruCtl ctl, char *filename, int64 segpage, void *data)
51 : {
52 2 : elog(NOTICE, "Calling test_slru_scan_cb()");
53 2 : return SlruScanDirCbDeleteAll(ctl, filename, segpage, data);
54 : }
55 :
56 : Datum
57 196 : test_slru_page_write(PG_FUNCTION_ARGS)
58 : {
59 196 : int64 pageno = PG_GETARG_INT64(0);
60 196 : char *data = text_to_cstring(PG_GETARG_TEXT_PP(1));
61 : int slotno;
62 196 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
63 :
64 196 : LWLockAcquire(lock, LW_EXCLUSIVE);
65 196 : slotno = SimpleLruZeroPage(TestSlruCtl, pageno);
66 :
67 : /* these should match */
68 : Assert(TestSlruCtl->shared->page_number[slotno] == pageno);
69 :
70 : /* mark the page as dirty so as it would get written */
71 196 : TestSlruCtl->shared->page_dirty[slotno] = true;
72 196 : TestSlruCtl->shared->page_status[slotno] = SLRU_PAGE_VALID;
73 :
74 : /* write given data to the page, up to the limit of the page */
75 196 : strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
76 : BLCKSZ - 1);
77 :
78 196 : SimpleLruWritePage(TestSlruCtl, slotno);
79 196 : LWLockRelease(lock);
80 :
81 196 : PG_RETURN_VOID();
82 : }
83 :
84 : Datum
85 4 : test_slru_page_writeall(PG_FUNCTION_ARGS)
86 : {
87 4 : SimpleLruWriteAll(TestSlruCtl, true);
88 4 : PG_RETURN_VOID();
89 : }
90 :
91 : Datum
92 8 : test_slru_page_read(PG_FUNCTION_ARGS)
93 : {
94 8 : int64 pageno = PG_GETARG_INT64(0);
95 8 : bool write_ok = PG_GETARG_BOOL(1);
96 8 : char *data = NULL;
97 : int slotno;
98 8 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
99 :
100 : /* find page in buffers, reading it if necessary */
101 8 : LWLockAcquire(lock, LW_EXCLUSIVE);
102 8 : slotno = SimpleLruReadPage(TestSlruCtl, pageno,
103 : write_ok, InvalidTransactionId);
104 8 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
105 8 : LWLockRelease(lock);
106 :
107 8 : PG_RETURN_TEXT_P(cstring_to_text(data));
108 : }
109 :
110 : Datum
111 8 : test_slru_page_readonly(PG_FUNCTION_ARGS)
112 : {
113 8 : int64 pageno = PG_GETARG_INT64(0);
114 8 : char *data = NULL;
115 : int slotno;
116 8 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
117 :
118 : /* find page in buffers, reading it if necessary */
119 8 : slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
120 : pageno,
121 : InvalidTransactionId);
122 : Assert(LWLockHeldByMe(lock));
123 8 : data = (char *) TestSlruCtl->shared->page_buffer[slotno];
124 8 : LWLockRelease(lock);
125 :
126 8 : PG_RETURN_TEXT_P(cstring_to_text(data));
127 : }
128 :
129 : Datum
130 36 : test_slru_page_exists(PG_FUNCTION_ARGS)
131 : {
132 36 : int64 pageno = PG_GETARG_INT64(0);
133 : bool found;
134 36 : LWLock *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
135 :
136 36 : LWLockAcquire(lock, LW_EXCLUSIVE);
137 36 : found = SimpleLruDoesPhysicalPageExist(TestSlruCtl, pageno);
138 36 : LWLockRelease(lock);
139 :
140 36 : PG_RETURN_BOOL(found);
141 : }
142 :
143 : Datum
144 4 : test_slru_page_sync(PG_FUNCTION_ARGS)
145 : {
146 4 : int64 pageno = PG_GETARG_INT64(0);
147 : FileTag ftag;
148 : char path[MAXPGPATH];
149 :
150 : /* note that this flushes the full file a segment is located in */
151 4 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
152 4 : SlruSyncFileTag(TestSlruCtl, &ftag, path);
153 :
154 4 : elog(NOTICE, "Called SlruSyncFileTag() for segment %lld on path %s",
155 : (long long) ftag.segno, path);
156 :
157 4 : PG_RETURN_VOID();
158 : }
159 :
160 : Datum
161 4 : test_slru_page_delete(PG_FUNCTION_ARGS)
162 : {
163 4 : int64 pageno = PG_GETARG_INT64(0);
164 : FileTag ftag;
165 :
166 4 : ftag.segno = pageno / SLRU_PAGES_PER_SEGMENT;
167 4 : SlruDeleteSegment(TestSlruCtl, ftag.segno);
168 :
169 4 : elog(NOTICE, "Called SlruDeleteSegment() for segment %lld",
170 : (long long) ftag.segno);
171 :
172 4 : PG_RETURN_VOID();
173 : }
174 :
175 : Datum
176 4 : test_slru_page_truncate(PG_FUNCTION_ARGS)
177 : {
178 4 : int64 pageno = PG_GETARG_INT64(0);
179 :
180 4 : SimpleLruTruncate(TestSlruCtl, pageno);
181 4 : PG_RETURN_VOID();
182 : }
183 :
184 : Datum
185 4 : test_slru_delete_all(PG_FUNCTION_ARGS)
186 : {
187 : /* this calls SlruScanDirCbDeleteAll() internally, ensuring deletion */
188 4 : SlruScanDirectory(TestSlruCtl, test_slru_scan_cb, NULL);
189 :
190 4 : PG_RETURN_VOID();
191 : }
192 :
193 : /*
194 : * Module load callbacks and initialization.
195 : */
196 :
197 : static void
198 4 : test_slru_shmem_request(void)
199 : {
200 4 : if (prev_shmem_request_hook)
201 0 : prev_shmem_request_hook();
202 :
203 : /* reserve shared memory for the test SLRU */
204 4 : RequestAddinShmemSpace(SimpleLruShmemSize(NUM_TEST_BUFFERS, 0));
205 4 : }
206 :
207 : static bool
208 30 : test_slru_page_precedes_logically(int64 page1, int64 page2)
209 : {
210 30 : return page1 < page2;
211 : }
212 :
213 : static void
214 4 : test_slru_shmem_startup(void)
215 : {
216 : /*
217 : * Short segments names are well tested elsewhere so in this test we are
218 : * focusing on long names.
219 : */
220 4 : const bool long_segment_names = true;
221 4 : const char slru_dir_name[] = "pg_test_slru";
222 : int test_tranche_id;
223 : int test_buffer_tranche_id;
224 :
225 4 : if (prev_shmem_startup_hook)
226 0 : prev_shmem_startup_hook();
227 :
228 : /*
229 : * Create the SLRU directory if it does not exist yet, from the root of
230 : * the data directory.
231 : */
232 4 : (void) MakePGDirectory(slru_dir_name);
233 :
234 : /* initialize the SLRU facility */
235 4 : test_tranche_id = LWLockNewTrancheId();
236 4 : LWLockRegisterTranche(test_tranche_id, "test_slru_tranche");
237 :
238 4 : test_buffer_tranche_id = LWLockNewTrancheId();
239 4 : LWLockRegisterTranche(test_tranche_id, "test_buffer_tranche");
240 :
241 4 : TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
242 4 : SimpleLruInit(TestSlruCtl, "TestSLRU",
243 : NUM_TEST_BUFFERS, 0, slru_dir_name,
244 : test_buffer_tranche_id, test_tranche_id, SYNC_HANDLER_NONE,
245 : long_segment_names);
246 4 : }
247 :
248 : void
249 4 : _PG_init(void)
250 : {
251 4 : if (!process_shared_preload_libraries_in_progress)
252 0 : ereport(ERROR,
253 : (errmsg("cannot load \"%s\" after startup", "test_slru"),
254 : errdetail("\"%s\" must be loaded with \"shared_preload_libraries\".",
255 : "test_slru")));
256 :
257 4 : prev_shmem_request_hook = shmem_request_hook;
258 4 : shmem_request_hook = test_slru_shmem_request;
259 :
260 4 : prev_shmem_startup_hook = shmem_startup_hook;
261 4 : shmem_startup_hook = test_slru_shmem_startup;
262 4 : }
|