Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * spccache.c
4 : * Tablespace cache management.
5 : *
6 : * We cache the parsed version of spcoptions for each tablespace to avoid
7 : * needing to reparse on every lookup. Right now, there doesn't appear to
8 : * be a measurable performance gain from doing this, but that might change
9 : * in the future as we add more options.
10 : *
11 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
12 : * Portions Copyright (c) 1994, Regents of the University of California
13 : *
14 : * IDENTIFICATION
15 : * src/backend/utils/cache/spccache.c
16 : *
17 : *-------------------------------------------------------------------------
18 : */
19 : #include "postgres.h"
20 :
21 : #include "access/reloptions.h"
22 : #include "catalog/pg_tablespace.h"
23 : #include "commands/tablespace.h"
24 : #include "miscadmin.h"
25 : #include "optimizer/optimizer.h"
26 : #include "storage/bufmgr.h"
27 : #include "utils/catcache.h"
28 : #include "utils/hsearch.h"
29 : #include "utils/inval.h"
30 : #include "utils/spccache.h"
31 : #include "utils/syscache.h"
32 : #include "varatt.h"
33 :
34 :
35 : /* Hash table for information about each tablespace */
36 : static HTAB *TableSpaceCacheHash = NULL;
37 :
38 : typedef struct
39 : {
40 : Oid oid; /* lookup key - must be first */
41 : TableSpaceOpts *opts; /* options, or NULL if none */
42 : } TableSpaceCacheEntry;
43 :
44 :
45 : /*
46 : * InvalidateTableSpaceCacheCallback
47 : * Flush all cache entries when pg_tablespace is updated.
48 : *
49 : * When pg_tablespace is updated, we must flush the cache entry at least
50 : * for that tablespace. Currently, we just flush them all. This is quick
51 : * and easy and doesn't cost much, since there shouldn't be terribly many
52 : * tablespaces, nor do we expect them to be frequently modified.
53 : */
54 : static void
55 426 : InvalidateTableSpaceCacheCallback(Datum arg, SysCacheIdentifier cacheid,
56 : uint32 hashvalue)
57 : {
58 : HASH_SEQ_STATUS status;
59 : TableSpaceCacheEntry *spc;
60 :
61 426 : hash_seq_init(&status, TableSpaceCacheHash);
62 618 : while ((spc = (TableSpaceCacheEntry *) hash_seq_search(&status)) != NULL)
63 : {
64 192 : if (spc->opts)
65 0 : pfree(spc->opts);
66 192 : if (hash_search(TableSpaceCacheHash,
67 192 : &spc->oid,
68 : HASH_REMOVE,
69 : NULL) == NULL)
70 0 : elog(ERROR, "hash table corrupted");
71 : }
72 426 : }
73 :
74 : /*
75 : * InitializeTableSpaceCache
76 : * Initialize the tablespace cache.
77 : */
78 : static void
79 8185 : InitializeTableSpaceCache(void)
80 : {
81 : HASHCTL ctl;
82 :
83 : /* Initialize the hash table. */
84 8185 : ctl.keysize = sizeof(Oid);
85 8185 : ctl.entrysize = sizeof(TableSpaceCacheEntry);
86 8185 : TableSpaceCacheHash =
87 8185 : hash_create("TableSpace cache", 16, &ctl,
88 : HASH_ELEM | HASH_BLOBS);
89 :
90 : /* Make sure we've initialized CacheMemoryContext. */
91 8185 : if (!CacheMemoryContext)
92 0 : CreateCacheMemoryContext();
93 :
94 : /* Watch for invalidation events. */
95 8185 : CacheRegisterSyscacheCallback(TABLESPACEOID,
96 : InvalidateTableSpaceCacheCallback,
97 : (Datum) 0);
98 8185 : }
99 :
100 : /*
101 : * get_tablespace
102 : * Fetch TableSpaceCacheEntry structure for a specified table OID.
103 : *
104 : * Pointers returned by this function should not be stored, since a cache
105 : * flush will invalidate them.
106 : */
107 : static TableSpaceCacheEntry *
108 1637021 : get_tablespace(Oid spcid)
109 : {
110 : TableSpaceCacheEntry *spc;
111 : HeapTuple tp;
112 : TableSpaceOpts *opts;
113 :
114 : /*
115 : * Since spcid is always from a pg_class tuple, InvalidOid implies the
116 : * default.
117 : */
118 1637021 : if (spcid == InvalidOid)
119 1469154 : spcid = MyDatabaseTableSpace;
120 :
121 : /* Find existing cache entry, if any. */
122 1637021 : if (!TableSpaceCacheHash)
123 8185 : InitializeTableSpaceCache();
124 1637021 : spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
125 : &spcid,
126 : HASH_FIND,
127 : NULL);
128 1637021 : if (spc)
129 1627919 : return spc;
130 :
131 : /*
132 : * Not found in TableSpace cache. Check catcache. If we don't find a
133 : * valid HeapTuple, it must mean someone has managed to request tablespace
134 : * details for a non-existent tablespace. We'll just treat that case as
135 : * if no options were specified.
136 : */
137 9102 : tp = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spcid));
138 9102 : if (!HeapTupleIsValid(tp))
139 0 : opts = NULL;
140 : else
141 : {
142 : Datum datum;
143 : bool isNull;
144 :
145 9102 : datum = SysCacheGetAttr(TABLESPACEOID,
146 : tp,
147 : Anum_pg_tablespace_spcoptions,
148 : &isNull);
149 9102 : if (isNull)
150 9099 : opts = NULL;
151 : else
152 : {
153 3 : bytea *bytea_opts = tablespace_reloptions(datum, false);
154 :
155 3 : opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
156 3 : memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
157 : }
158 9102 : ReleaseSysCache(tp);
159 : }
160 :
161 : /*
162 : * Now create the cache entry. It's important to do this only after
163 : * reading the pg_tablespace entry, since doing so could cause a cache
164 : * flush.
165 : */
166 9102 : spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
167 : &spcid,
168 : HASH_ENTER,
169 : NULL);
170 9102 : spc->opts = opts;
171 9102 : return spc;
172 : }
173 :
174 : /*
175 : * get_tablespace_page_costs
176 : * Return random and/or sequential page costs for a given tablespace.
177 : *
178 : * This value is not locked by the transaction, so this value may
179 : * be changed while a SELECT that has used these values for planning
180 : * is still executing.
181 : */
182 : void
183 1532639 : get_tablespace_page_costs(Oid spcid,
184 : double *spc_random_page_cost,
185 : double *spc_seq_page_cost)
186 : {
187 1532639 : TableSpaceCacheEntry *spc = get_tablespace(spcid);
188 :
189 : Assert(spc != NULL);
190 :
191 1532639 : if (spc_random_page_cost)
192 : {
193 1289897 : if (!spc->opts || spc->opts->random_page_cost < 0)
194 1289897 : *spc_random_page_cost = random_page_cost;
195 : else
196 0 : *spc_random_page_cost = spc->opts->random_page_cost;
197 : }
198 :
199 1532639 : if (spc_seq_page_cost)
200 : {
201 1056776 : if (!spc->opts || spc->opts->seq_page_cost < 0)
202 1056647 : *spc_seq_page_cost = seq_page_cost;
203 : else
204 129 : *spc_seq_page_cost = spc->opts->seq_page_cost;
205 : }
206 1532639 : }
207 :
208 : /*
209 : * get_tablespace_io_concurrency
210 : *
211 : * This value is not locked by the transaction, so this value may
212 : * be changed while a SELECT that has used these values for planning
213 : * is still executing.
214 : */
215 : int
216 86257 : get_tablespace_io_concurrency(Oid spcid)
217 : {
218 86257 : TableSpaceCacheEntry *spc = get_tablespace(spcid);
219 :
220 86257 : if (!spc->opts || spc->opts->effective_io_concurrency < 0)
221 86257 : return effective_io_concurrency;
222 : else
223 0 : return spc->opts->effective_io_concurrency;
224 : }
225 :
226 : /*
227 : * get_tablespace_maintenance_io_concurrency
228 : */
229 : int
230 18125 : get_tablespace_maintenance_io_concurrency(Oid spcid)
231 : {
232 18125 : TableSpaceCacheEntry *spc = get_tablespace(spcid);
233 :
234 18125 : if (!spc->opts || spc->opts->maintenance_io_concurrency < 0)
235 18125 : return maintenance_io_concurrency;
236 : else
237 0 : return spc->opts->maintenance_io_concurrency;
238 : }
|